added support for clock settings in BD engine to level editor
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313
314   {
315     -1,                                 -1,
316     -1,                                 -1,
317     NULL,                               -1
318   }
319 };
320
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
322 {
323   // (these values are the same for each player)
324   {
325     EL_PLAYER_1,                        -1,
326     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
327     &li.block_last_field,               FALSE   // default case for EM levels
328   },
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
332     &li.sp_block_last_field,            TRUE    // default case for SP levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
337     &li.instant_relocation,             FALSE
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
342     &li.can_pass_to_walkable,           FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
347     &li.block_snap_field,               TRUE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
352     &li.continuous_snapping,            TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
357     &li.shifted_relocation,             FALSE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
362     &li.lazy_relocation,                FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
367     &li.finish_dig_collect,             TRUE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
372     &li.keep_walkable_ce,               FALSE
373   },
374
375   // (these values are different for each player)
376   {
377     EL_PLAYER_1,                        -1,
378     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
379     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
380   },
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
384     &li.initial_player_gravity[0],      FALSE
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
389     &li.use_start_element[0],           FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
394     &li.start_element[0],               EL_PLAYER_1
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
399     &li.use_artwork_element[0],         FALSE
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
404     &li.artwork_element[0],             EL_PLAYER_1
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
409     &li.use_explosion_element[0],       FALSE
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
414     &li.explosion_element[0],           EL_PLAYER_1
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
419     &li.use_initial_inventory[0],       FALSE
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
424     &li.initial_inventory_size[0],      1
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
429     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
431   },
432
433   {
434     EL_PLAYER_2,                        -1,
435     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
436     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
437   },
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
441     &li.initial_player_gravity[1],      FALSE
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
446     &li.use_start_element[1],           FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
451     &li.start_element[1],               EL_PLAYER_2
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
456     &li.use_artwork_element[1],         FALSE
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
461     &li.artwork_element[1],             EL_PLAYER_2
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
466     &li.use_explosion_element[1],       FALSE
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
471     &li.explosion_element[1],           EL_PLAYER_2
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
476     &li.use_initial_inventory[1],       FALSE
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
481     &li.initial_inventory_size[1],      1
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
486     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
488   },
489
490   {
491     EL_PLAYER_3,                        -1,
492     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
493     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
494   },
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
498     &li.initial_player_gravity[2],      FALSE
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
503     &li.use_start_element[2],           FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
508     &li.start_element[2],               EL_PLAYER_3
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
513     &li.use_artwork_element[2],         FALSE
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
518     &li.artwork_element[2],             EL_PLAYER_3
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
523     &li.use_explosion_element[2],       FALSE
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
528     &li.explosion_element[2],           EL_PLAYER_3
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
533     &li.use_initial_inventory[2],       FALSE
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
538     &li.initial_inventory_size[2],      1
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
543     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
545   },
546
547   {
548     EL_PLAYER_4,                        -1,
549     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
550     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
551   },
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
555     &li.initial_player_gravity[3],      FALSE
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
560     &li.use_start_element[3],           FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
565     &li.start_element[3],               EL_PLAYER_4
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
570     &li.use_artwork_element[3],         FALSE
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
575     &li.artwork_element[3],             EL_PLAYER_4
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
580     &li.use_explosion_element[3],       FALSE
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
585     &li.explosion_element[3],           EL_PLAYER_4
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
590     &li.use_initial_inventory[3],       FALSE
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
595     &li.initial_inventory_size[3],      1
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
600     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
602   },
603
604   // (these values are only valid for BD style levels)
605   // (some values for BD style amoeba following below)
606   {
607     EL_BD_PLAYER,                       -1,
608     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
609     &li.bd_diagonal_movements,          FALSE
610   },
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
614     &li.bd_topmost_player_active,       TRUE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
619     &li.bd_pushing_prob,                25
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
624     &li.bd_pushing_prob_with_sweet,     100
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
629     &li.bd_push_mega_rock_with_sweet,   FALSE
630   },
631
632   {
633     EL_BD_DIAMOND,                      -1,
634     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
635     &li.score[SC_DIAMOND_EXTRA],        20
636   },
637
638   {
639     EL_BD_MAGIC_WALL,                   -1,
640     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
641     &li.bd_magic_wall_wait_hatching,    FALSE
642   },
643   {
644     EL_BD_MAGIC_WALL,                   -1,
645     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
646     &li.bd_magic_wall_stops_amoeba,     TRUE
647   },
648
649   {
650     EL_BD_CLOCK,                        -1,
651     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
652     &li.bd_clock_extra_time,            30
653   },
654
655   // (the following values are related to various game elements)
656
657   {
658     EL_EMERALD,                         -1,
659     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
660     &li.score[SC_EMERALD],              10
661   },
662
663   {
664     EL_DIAMOND,                         -1,
665     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
666     &li.score[SC_DIAMOND],              10
667   },
668
669   {
670     EL_BUG,                             -1,
671     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
672     &li.score[SC_BUG],                  10
673   },
674
675   {
676     EL_SPACESHIP,                       -1,
677     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
678     &li.score[SC_SPACESHIP],            10
679   },
680
681   {
682     EL_PACMAN,                          -1,
683     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
684     &li.score[SC_PACMAN],               10
685   },
686
687   {
688     EL_NUT,                             -1,
689     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
690     &li.score[SC_NUT],                  10
691   },
692
693   {
694     EL_DYNAMITE,                        -1,
695     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
696     &li.score[SC_DYNAMITE],             10
697   },
698
699   {
700     EL_KEY_1,                           -1,
701     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
702     &li.score[SC_KEY],                  10
703   },
704
705   {
706     EL_PEARL,                           -1,
707     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
708     &li.score[SC_PEARL],                10
709   },
710
711   {
712     EL_CRYSTAL,                         -1,
713     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
714     &li.score[SC_CRYSTAL],              10
715   },
716
717   // (amoeba values used by R'n'D game engine only)
718   {
719     EL_BD_AMOEBA,                       -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.amoeba_content,                 EL_DIAMOND
722   },
723   {
724     EL_BD_AMOEBA,                       -1,
725     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
726     &li.amoeba_speed,                   10
727   },
728   {
729     EL_BD_AMOEBA,                       -1,
730     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
731     &li.grow_into_diggable,             TRUE
732   },
733   // (amoeba values used by BD game engine only)
734   {
735     EL_BD_AMOEBA,                       -1,
736     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
737     &li.bd_amoeba_wait_for_hatching,    FALSE
738   },
739   {
740     EL_BD_AMOEBA,                       -1,
741     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
742     &li.bd_amoeba_start_immediately,    TRUE
743   },
744   {
745     EL_BD_AMOEBA,                       -1,
746     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
747     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
748   },
749   {
750     EL_BD_AMOEBA,                       -1,
751     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
752     &li.bd_amoeba_threshold_too_big,    200
753   },
754   {
755     EL_BD_AMOEBA,                       -1,
756     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
757     &li.bd_amoeba_slow_growth_time,     200
758   },
759   {
760     EL_BD_AMOEBA,                       -1,
761     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
762     &li.bd_amoeba_slow_growth_rate,     3
763   },
764   {
765     EL_BD_AMOEBA,                       -1,
766     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
767     &li.bd_amoeba_fast_growth_rate,     25
768   },
769   {
770     EL_BD_AMOEBA,                       -1,
771     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
772     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
773   },
774   {
775     EL_BD_AMOEBA,                       -1,
776     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
777     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
778   },
779
780   {
781     EL_BD_AMOEBA_2,                     -1,
782     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
783     &li.bd_amoeba_2_threshold_too_big,  200
784   },
785   {
786     EL_BD_AMOEBA_2,                     -1,
787     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
788     &li.bd_amoeba_2_slow_growth_time,   200
789   },
790   {
791     EL_BD_AMOEBA_2,                     -1,
792     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
793     &li.bd_amoeba_2_slow_growth_rate,   3
794   },
795   {
796     EL_BD_AMOEBA_2,                     -1,
797     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
798     &li.bd_amoeba_2_fast_growth_rate,   25
799   },
800   {
801     EL_BD_AMOEBA_2,                     -1,
802     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
803     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
804   },
805   {
806     EL_BD_AMOEBA_2,                     -1,
807     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
808     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
809   },
810   {
811     EL_BD_AMOEBA_2,                     -1,
812     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
813     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
814   },
815   {
816     EL_BD_AMOEBA_2,                     -1,
817     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
818     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
819   },
820
821   {
822     EL_YAMYAM,                          -1,
823     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
824     &li.yamyam_content,                 EL_ROCK, NULL,
825     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
826   },
827   {
828     EL_YAMYAM,                          -1,
829     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
830     &li.score[SC_YAMYAM],               10
831   },
832
833   {
834     EL_ROBOT,                           -1,
835     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
836     &li.score[SC_ROBOT],                10
837   },
838   {
839     EL_ROBOT,                           -1,
840     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
841     &li.slurp_score,                    10
842   },
843
844   {
845     EL_ROBOT_WHEEL,                     -1,
846     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
847     &li.time_wheel,                     10
848   },
849
850   {
851     EL_MAGIC_WALL,                      -1,
852     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
853     &li.time_magic_wall,                10
854   },
855
856   {
857     EL_GAME_OF_LIFE,                    -1,
858     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
859     &li.game_of_life[0],                2
860   },
861   {
862     EL_GAME_OF_LIFE,                    -1,
863     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
864     &li.game_of_life[1],                3
865   },
866   {
867     EL_GAME_OF_LIFE,                    -1,
868     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
869     &li.game_of_life[2],                3
870   },
871   {
872     EL_GAME_OF_LIFE,                    -1,
873     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
874     &li.game_of_life[3],                3
875   },
876   {
877     EL_GAME_OF_LIFE,                    -1,
878     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
879     &li.use_life_bugs,                  FALSE
880   },
881
882   {
883     EL_BIOMAZE,                         -1,
884     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
885     &li.biomaze[0],                     2
886   },
887   {
888     EL_BIOMAZE,                         -1,
889     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
890     &li.biomaze[1],                     3
891   },
892   {
893     EL_BIOMAZE,                         -1,
894     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
895     &li.biomaze[2],                     3
896   },
897   {
898     EL_BIOMAZE,                         -1,
899     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
900     &li.biomaze[3],                     3
901   },
902
903   {
904     EL_TIMEGATE_SWITCH,                 -1,
905     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
906     &li.time_timegate,                  10
907   },
908
909   {
910     EL_LIGHT_SWITCH_ACTIVE,             -1,
911     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
912     &li.time_light,                     10
913   },
914
915   {
916     EL_SHIELD_NORMAL,                   -1,
917     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
918     &li.shield_normal_time,             10
919   },
920   {
921     EL_SHIELD_NORMAL,                   -1,
922     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
923     &li.score[SC_SHIELD],               10
924   },
925
926   {
927     EL_SHIELD_DEADLY,                   -1,
928     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
929     &li.shield_deadly_time,             10
930   },
931   {
932     EL_SHIELD_DEADLY,                   -1,
933     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
934     &li.score[SC_SHIELD],               10
935   },
936
937   {
938     EL_EXTRA_TIME,                      -1,
939     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
940     &li.extra_time,                     10
941   },
942   {
943     EL_EXTRA_TIME,                      -1,
944     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
945     &li.extra_time_score,               10
946   },
947
948   {
949     EL_TIME_ORB_FULL,                   -1,
950     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
951     &li.time_orb_time,                  10
952   },
953   {
954     EL_TIME_ORB_FULL,                   -1,
955     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
956     &li.use_time_orb_bug,               FALSE
957   },
958
959   {
960     EL_SPRING,                          -1,
961     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
962     &li.use_spring_bug,                 FALSE
963   },
964
965   {
966     EL_EMC_ANDROID,                     -1,
967     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
968     &li.android_move_time,              10
969   },
970   {
971     EL_EMC_ANDROID,                     -1,
972     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
973     &li.android_clone_time,             10
974   },
975   {
976     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
977     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
978     &li.android_clone_element[0],       EL_EMPTY, NULL,
979     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
980   },
981   {
982     EL_EMC_ANDROID,                     -1,
983     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
984     &li.android_clone_element[0],       EL_EMPTY, NULL,
985     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
986   },
987
988   {
989     EL_EMC_LENSES,                      -1,
990     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
991     &li.lenses_score,                   10
992   },
993   {
994     EL_EMC_LENSES,                      -1,
995     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
996     &li.lenses_time,                    10
997   },
998
999   {
1000     EL_EMC_MAGNIFIER,                   -1,
1001     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1002     &li.magnify_score,                  10
1003   },
1004   {
1005     EL_EMC_MAGNIFIER,                   -1,
1006     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1007     &li.magnify_time,                   10
1008   },
1009
1010   {
1011     EL_EMC_MAGIC_BALL,                  -1,
1012     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1013     &li.ball_time,                      10
1014   },
1015   {
1016     EL_EMC_MAGIC_BALL,                  -1,
1017     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1018     &li.ball_random,                    FALSE
1019   },
1020   {
1021     EL_EMC_MAGIC_BALL,                  -1,
1022     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1023     &li.ball_active_initial,            FALSE
1024   },
1025   {
1026     EL_EMC_MAGIC_BALL,                  -1,
1027     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1028     &li.ball_content,                   EL_EMPTY, NULL,
1029     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1030   },
1031
1032   {
1033     EL_SOKOBAN_FIELD_EMPTY,             -1,
1034     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1035     &li.sb_fields_needed,               TRUE
1036   },
1037
1038   {
1039     EL_SOKOBAN_OBJECT,                  -1,
1040     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1041     &li.sb_objects_needed,              TRUE
1042   },
1043
1044   {
1045     EL_MM_MCDUFFIN,                     -1,
1046     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1047     &li.mm_laser_red,                   FALSE
1048   },
1049   {
1050     EL_MM_MCDUFFIN,                     -1,
1051     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1052     &li.mm_laser_green,                 FALSE
1053   },
1054   {
1055     EL_MM_MCDUFFIN,                     -1,
1056     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1057     &li.mm_laser_blue,                  TRUE
1058   },
1059
1060   {
1061     EL_DF_LASER,                        -1,
1062     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1063     &li.df_laser_red,                   TRUE
1064   },
1065   {
1066     EL_DF_LASER,                        -1,
1067     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1068     &li.df_laser_green,                 TRUE
1069   },
1070   {
1071     EL_DF_LASER,                        -1,
1072     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1073     &li.df_laser_blue,                  FALSE
1074   },
1075
1076   {
1077     EL_MM_FUSE_ACTIVE,                  -1,
1078     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1079     &li.mm_time_fuse,                   25
1080   },
1081   {
1082     EL_MM_BOMB,                         -1,
1083     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1084     &li.mm_time_bomb,                   75
1085   },
1086
1087   {
1088     EL_MM_GRAY_BALL,                    -1,
1089     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1090     &li.mm_time_ball,                   75
1091   },
1092   {
1093     EL_MM_GRAY_BALL,                    -1,
1094     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1095     &li.mm_ball_choice_mode,            ANIM_RANDOM
1096   },
1097   {
1098     EL_MM_GRAY_BALL,                    -1,
1099     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1100     &li.mm_ball_content,                EL_EMPTY, NULL,
1101     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1102   },
1103   {
1104     EL_MM_GRAY_BALL,                    -1,
1105     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1106     &li.rotate_mm_ball_content,         TRUE
1107   },
1108   {
1109     EL_MM_GRAY_BALL,                    -1,
1110     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1111     &li.explode_mm_ball,                FALSE
1112   },
1113
1114   {
1115     EL_MM_STEEL_BLOCK,                  -1,
1116     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1117     &li.mm_time_block,                  75
1118   },
1119   {
1120     EL_MM_LIGHTBALL,                    -1,
1121     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1122     &li.score[SC_ELEM_BONUS],           10
1123   },
1124
1125   {
1126     -1,                                 -1,
1127     -1,                                 -1,
1128     NULL,                               -1
1129   }
1130 };
1131
1132 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1133 {
1134   {
1135     -1,                                 -1,
1136     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1137     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1138   },
1139   {
1140     -1,                                 -1,
1141     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1142     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1143   },
1144
1145   {
1146     -1,                                 -1,
1147     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1148     &xx_envelope.autowrap,              FALSE
1149   },
1150   {
1151     -1,                                 -1,
1152     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1153     &xx_envelope.centered,              FALSE
1154   },
1155
1156   {
1157     -1,                                 -1,
1158     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1159     &xx_envelope.text,                  -1, NULL,
1160     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1161     &xx_default_string_empty[0]
1162   },
1163
1164   {
1165     -1,                                 -1,
1166     -1,                                 -1,
1167     NULL,                               -1
1168   }
1169 };
1170
1171 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1172 {
1173   {
1174     -1,                                 -1,
1175     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1176     &xx_ei.description[0],              -1,
1177     &yy_ei.description[0],
1178     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1179     &xx_default_description[0]
1180   },
1181
1182   {
1183     -1,                                 -1,
1184     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1185     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1186     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1187   },
1188 #if ENABLE_RESERVED_CODE
1189   // (reserved for later use)
1190   {
1191     -1,                                 -1,
1192     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1193     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1194     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1195   },
1196 #endif
1197
1198   {
1199     -1,                                 -1,
1200     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1201     &xx_ei.use_gfx_element,             FALSE,
1202     &yy_ei.use_gfx_element
1203   },
1204   {
1205     -1,                                 -1,
1206     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1207     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1208     &yy_ei.gfx_element_initial
1209   },
1210
1211   {
1212     -1,                                 -1,
1213     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1214     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1215     &yy_ei.access_direction
1216   },
1217
1218   {
1219     -1,                                 -1,
1220     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1221     &xx_ei.collect_score_initial,       10,
1222     &yy_ei.collect_score_initial
1223   },
1224   {
1225     -1,                                 -1,
1226     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1227     &xx_ei.collect_count_initial,       1,
1228     &yy_ei.collect_count_initial
1229   },
1230
1231   {
1232     -1,                                 -1,
1233     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1234     &xx_ei.ce_value_fixed_initial,      0,
1235     &yy_ei.ce_value_fixed_initial
1236   },
1237   {
1238     -1,                                 -1,
1239     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1240     &xx_ei.ce_value_random_initial,     0,
1241     &yy_ei.ce_value_random_initial
1242   },
1243   {
1244     -1,                                 -1,
1245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1246     &xx_ei.use_last_ce_value,           FALSE,
1247     &yy_ei.use_last_ce_value
1248   },
1249
1250   {
1251     -1,                                 -1,
1252     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1253     &xx_ei.push_delay_fixed,            8,
1254     &yy_ei.push_delay_fixed
1255   },
1256   {
1257     -1,                                 -1,
1258     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1259     &xx_ei.push_delay_random,           8,
1260     &yy_ei.push_delay_random
1261   },
1262   {
1263     -1,                                 -1,
1264     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1265     &xx_ei.drop_delay_fixed,            0,
1266     &yy_ei.drop_delay_fixed
1267   },
1268   {
1269     -1,                                 -1,
1270     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1271     &xx_ei.drop_delay_random,           0,
1272     &yy_ei.drop_delay_random
1273   },
1274   {
1275     -1,                                 -1,
1276     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1277     &xx_ei.move_delay_fixed,            0,
1278     &yy_ei.move_delay_fixed
1279   },
1280   {
1281     -1,                                 -1,
1282     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1283     &xx_ei.move_delay_random,           0,
1284     &yy_ei.move_delay_random
1285   },
1286   {
1287     -1,                                 -1,
1288     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1289     &xx_ei.step_delay_fixed,            0,
1290     &yy_ei.step_delay_fixed
1291   },
1292   {
1293     -1,                                 -1,
1294     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1295     &xx_ei.step_delay_random,           0,
1296     &yy_ei.step_delay_random
1297   },
1298
1299   {
1300     -1,                                 -1,
1301     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1302     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1303     &yy_ei.move_pattern
1304   },
1305   {
1306     -1,                                 -1,
1307     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1308     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1309     &yy_ei.move_direction_initial
1310   },
1311   {
1312     -1,                                 -1,
1313     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1314     &xx_ei.move_stepsize,               TILEX / 8,
1315     &yy_ei.move_stepsize
1316   },
1317
1318   {
1319     -1,                                 -1,
1320     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1321     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1322     &yy_ei.move_enter_element
1323   },
1324   {
1325     -1,                                 -1,
1326     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1327     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1328     &yy_ei.move_leave_element
1329   },
1330   {
1331     -1,                                 -1,
1332     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1333     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1334     &yy_ei.move_leave_type
1335   },
1336
1337   {
1338     -1,                                 -1,
1339     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1340     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1341     &yy_ei.slippery_type
1342   },
1343
1344   {
1345     -1,                                 -1,
1346     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1347     &xx_ei.explosion_type,              EXPLODES_3X3,
1348     &yy_ei.explosion_type
1349   },
1350   {
1351     -1,                                 -1,
1352     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1353     &xx_ei.explosion_delay,             16,
1354     &yy_ei.explosion_delay
1355   },
1356   {
1357     -1,                                 -1,
1358     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1359     &xx_ei.ignition_delay,              8,
1360     &yy_ei.ignition_delay
1361   },
1362
1363   {
1364     -1,                                 -1,
1365     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1366     &xx_ei.content,                     EL_EMPTY_SPACE,
1367     &yy_ei.content,
1368     &xx_num_contents,                   1, 1
1369   },
1370
1371   // ---------- "num_change_pages" must be the last entry ---------------------
1372
1373   {
1374     -1,                                 SAVE_CONF_ALWAYS,
1375     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1376     &xx_ei.num_change_pages,            1,
1377     &yy_ei.num_change_pages
1378   },
1379
1380   {
1381     -1,                                 -1,
1382     -1,                                 -1,
1383     NULL,                               -1,
1384     NULL
1385   }
1386 };
1387
1388 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1389 {
1390   // ---------- "current_change_page" must be the first entry -----------------
1391
1392   {
1393     -1,                                 SAVE_CONF_ALWAYS,
1394     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1395     &xx_current_change_page,            -1
1396   },
1397
1398   // ---------- (the remaining entries can be in any order) -------------------
1399
1400   {
1401     -1,                                 -1,
1402     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1403     &xx_change.can_change,              FALSE
1404   },
1405
1406   {
1407     -1,                                 -1,
1408     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1409     &xx_event_bits[0],                  0
1410   },
1411   {
1412     -1,                                 -1,
1413     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1414     &xx_event_bits[1],                  0
1415   },
1416
1417   {
1418     -1,                                 -1,
1419     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1420     &xx_change.trigger_player,          CH_PLAYER_ANY
1421   },
1422   {
1423     -1,                                 -1,
1424     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1425     &xx_change.trigger_side,            CH_SIDE_ANY
1426   },
1427   {
1428     -1,                                 -1,
1429     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1430     &xx_change.trigger_page,            CH_PAGE_ANY
1431   },
1432
1433   {
1434     -1,                                 -1,
1435     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1436     &xx_change.target_element,          EL_EMPTY_SPACE
1437   },
1438
1439   {
1440     -1,                                 -1,
1441     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1442     &xx_change.delay_fixed,             0
1443   },
1444   {
1445     -1,                                 -1,
1446     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1447     &xx_change.delay_random,            0
1448   },
1449   {
1450     -1,                                 -1,
1451     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1452     &xx_change.delay_frames,            FRAMES_PER_SECOND
1453   },
1454
1455   {
1456     -1,                                 -1,
1457     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1458     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1459   },
1460
1461   {
1462     -1,                                 -1,
1463     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1464     &xx_change.explode,                 FALSE
1465   },
1466   {
1467     -1,                                 -1,
1468     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1469     &xx_change.use_target_content,      FALSE
1470   },
1471   {
1472     -1,                                 -1,
1473     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1474     &xx_change.only_if_complete,        FALSE
1475   },
1476   {
1477     -1,                                 -1,
1478     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1479     &xx_change.use_random_replace,      FALSE
1480   },
1481   {
1482     -1,                                 -1,
1483     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1484     &xx_change.random_percentage,       100
1485   },
1486   {
1487     -1,                                 -1,
1488     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1489     &xx_change.replace_when,            CP_WHEN_EMPTY
1490   },
1491
1492   {
1493     -1,                                 -1,
1494     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1495     &xx_change.has_action,              FALSE
1496   },
1497   {
1498     -1,                                 -1,
1499     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1500     &xx_change.action_type,             CA_NO_ACTION
1501   },
1502   {
1503     -1,                                 -1,
1504     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1505     &xx_change.action_mode,             CA_MODE_UNDEFINED
1506   },
1507   {
1508     -1,                                 -1,
1509     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1510     &xx_change.action_arg,              CA_ARG_UNDEFINED
1511   },
1512
1513   {
1514     -1,                                 -1,
1515     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1516     &xx_change.action_element,          EL_EMPTY_SPACE
1517   },
1518
1519   {
1520     -1,                                 -1,
1521     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1522     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1523     &xx_num_contents,                   1, 1
1524   },
1525
1526   {
1527     -1,                                 -1,
1528     -1,                                 -1,
1529     NULL,                               -1
1530   }
1531 };
1532
1533 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1534 {
1535   {
1536     -1,                                 -1,
1537     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1538     &xx_ei.description[0],              -1, NULL,
1539     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1540     &xx_default_description[0]
1541   },
1542
1543   {
1544     -1,                                 -1,
1545     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1546     &xx_ei.use_gfx_element,             FALSE
1547   },
1548   {
1549     -1,                                 -1,
1550     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1551     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1552   },
1553
1554   {
1555     -1,                                 -1,
1556     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1557     &xx_group.choice_mode,              ANIM_RANDOM
1558   },
1559
1560   {
1561     -1,                                 -1,
1562     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1563     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1564     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1565   },
1566
1567   {
1568     -1,                                 -1,
1569     -1,                                 -1,
1570     NULL,                               -1
1571   }
1572 };
1573
1574 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1575 {
1576   {
1577     -1,                                 -1,
1578     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1579     &xx_ei.use_gfx_element,             FALSE
1580   },
1581   {
1582     -1,                                 -1,
1583     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1584     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1585   },
1586
1587   {
1588     -1,                                 -1,
1589     -1,                                 -1,
1590     NULL,                               -1
1591   }
1592 };
1593
1594 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1595 {
1596   {
1597     EL_PLAYER_1,                        -1,
1598     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1599     &li.block_snap_field,               TRUE
1600   },
1601   {
1602     EL_PLAYER_1,                        -1,
1603     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1604     &li.continuous_snapping,            TRUE
1605   },
1606   {
1607     EL_PLAYER_1,                        -1,
1608     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1609     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1610   },
1611   {
1612     EL_PLAYER_1,                        -1,
1613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1614     &li.use_start_element[0],           FALSE
1615   },
1616   {
1617     EL_PLAYER_1,                        -1,
1618     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1619     &li.start_element[0],               EL_PLAYER_1
1620   },
1621   {
1622     EL_PLAYER_1,                        -1,
1623     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1624     &li.use_artwork_element[0],         FALSE
1625   },
1626   {
1627     EL_PLAYER_1,                        -1,
1628     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1629     &li.artwork_element[0],             EL_PLAYER_1
1630   },
1631   {
1632     EL_PLAYER_1,                        -1,
1633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1634     &li.use_explosion_element[0],       FALSE
1635   },
1636   {
1637     EL_PLAYER_1,                        -1,
1638     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1639     &li.explosion_element[0],           EL_PLAYER_1
1640   },
1641
1642   {
1643     -1,                                 -1,
1644     -1,                                 -1,
1645     NULL,                               -1
1646   }
1647 };
1648
1649 static struct
1650 {
1651   int filetype;
1652   char *id;
1653 }
1654 filetype_id_list[] =
1655 {
1656   { LEVEL_FILE_TYPE_RND,        "RND"   },
1657   { LEVEL_FILE_TYPE_BD,         "BD"    },
1658   { LEVEL_FILE_TYPE_EM,         "EM"    },
1659   { LEVEL_FILE_TYPE_SP,         "SP"    },
1660   { LEVEL_FILE_TYPE_DX,         "DX"    },
1661   { LEVEL_FILE_TYPE_SB,         "SB"    },
1662   { LEVEL_FILE_TYPE_DC,         "DC"    },
1663   { LEVEL_FILE_TYPE_MM,         "MM"    },
1664   { LEVEL_FILE_TYPE_MM,         "DF"    },
1665   { -1,                         NULL    },
1666 };
1667
1668
1669 // ============================================================================
1670 // level file functions
1671 // ============================================================================
1672
1673 static boolean check_special_flags(char *flag)
1674 {
1675   if (strEqual(options.special_flags, flag) ||
1676       strEqual(leveldir_current->special_flags, flag))
1677     return TRUE;
1678
1679   return FALSE;
1680 }
1681
1682 static struct DateInfo getCurrentDate(void)
1683 {
1684   time_t epoch_seconds = time(NULL);
1685   struct tm *now = localtime(&epoch_seconds);
1686   struct DateInfo date;
1687
1688   date.year  = now->tm_year + 1900;
1689   date.month = now->tm_mon  + 1;
1690   date.day   = now->tm_mday;
1691
1692   date.src   = DATE_SRC_CLOCK;
1693
1694   return date;
1695 }
1696
1697 static void resetEventFlags(struct ElementChangeInfo *change)
1698 {
1699   int i;
1700
1701   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1702     change->has_event[i] = FALSE;
1703 }
1704
1705 static void resetEventBits(void)
1706 {
1707   int i;
1708
1709   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1710     xx_event_bits[i] = 0;
1711 }
1712
1713 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1714 {
1715   int i;
1716
1717   /* important: only change event flag if corresponding event bit is set
1718      (this is because all xx_event_bits[] values are loaded separately,
1719      and all xx_event_bits[] values are set back to zero before loading
1720      another value xx_event_bits[x] (each value representing 32 flags)) */
1721
1722   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1723     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1724       change->has_event[i] = TRUE;
1725 }
1726
1727 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1728 {
1729   int i;
1730
1731   /* in contrast to the above function setEventFlagsFromEventBits(), it
1732      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1733      depending on the corresponding change->has_event[i] values here, as
1734      all xx_event_bits[] values are reset in resetEventBits() before */
1735
1736   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1737     if (change->has_event[i])
1738       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1739 }
1740
1741 static char *getDefaultElementDescription(struct ElementInfo *ei)
1742 {
1743   static char description[MAX_ELEMENT_NAME_LEN + 1];
1744   char *default_description = (ei->custom_description != NULL ?
1745                                ei->custom_description :
1746                                ei->editor_description);
1747   int i;
1748
1749   // always start with reliable default values
1750   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1751     description[i] = '\0';
1752
1753   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1754   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1755
1756   return &description[0];
1757 }
1758
1759 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1760 {
1761   char *default_description = getDefaultElementDescription(ei);
1762   int i;
1763
1764   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1765     ei->description[i] = default_description[i];
1766 }
1767
1768 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1769 {
1770   int i;
1771
1772   for (i = 0; conf[i].data_type != -1; i++)
1773   {
1774     int default_value = conf[i].default_value;
1775     int data_type = conf[i].data_type;
1776     int conf_type = conf[i].conf_type;
1777     int byte_mask = conf_type & CONF_MASK_BYTES;
1778
1779     if (byte_mask == CONF_MASK_MULTI_BYTES)
1780     {
1781       int default_num_entities = conf[i].default_num_entities;
1782       int max_num_entities = conf[i].max_num_entities;
1783
1784       *(int *)(conf[i].num_entities) = default_num_entities;
1785
1786       if (data_type == TYPE_STRING)
1787       {
1788         char *default_string = conf[i].default_string;
1789         char *string = (char *)(conf[i].value);
1790
1791         strncpy(string, default_string, max_num_entities);
1792       }
1793       else if (data_type == TYPE_ELEMENT_LIST)
1794       {
1795         int *element_array = (int *)(conf[i].value);
1796         int j;
1797
1798         for (j = 0; j < max_num_entities; j++)
1799           element_array[j] = default_value;
1800       }
1801       else if (data_type == TYPE_CONTENT_LIST)
1802       {
1803         struct Content *content = (struct Content *)(conf[i].value);
1804         int c, x, y;
1805
1806         for (c = 0; c < max_num_entities; c++)
1807           for (y = 0; y < 3; y++)
1808             for (x = 0; x < 3; x++)
1809               content[c].e[x][y] = default_value;
1810       }
1811     }
1812     else        // constant size configuration data (1, 2 or 4 bytes)
1813     {
1814       if (data_type == TYPE_BOOLEAN)
1815         *(boolean *)(conf[i].value) = default_value;
1816       else
1817         *(int *)    (conf[i].value) = default_value;
1818     }
1819   }
1820 }
1821
1822 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1823 {
1824   int i;
1825
1826   for (i = 0; conf[i].data_type != -1; i++)
1827   {
1828     int data_type = conf[i].data_type;
1829     int conf_type = conf[i].conf_type;
1830     int byte_mask = conf_type & CONF_MASK_BYTES;
1831
1832     if (byte_mask == CONF_MASK_MULTI_BYTES)
1833     {
1834       int max_num_entities = conf[i].max_num_entities;
1835
1836       if (data_type == TYPE_STRING)
1837       {
1838         char *string      = (char *)(conf[i].value);
1839         char *string_copy = (char *)(conf[i].value_copy);
1840
1841         strncpy(string_copy, string, max_num_entities);
1842       }
1843       else if (data_type == TYPE_ELEMENT_LIST)
1844       {
1845         int *element_array      = (int *)(conf[i].value);
1846         int *element_array_copy = (int *)(conf[i].value_copy);
1847         int j;
1848
1849         for (j = 0; j < max_num_entities; j++)
1850           element_array_copy[j] = element_array[j];
1851       }
1852       else if (data_type == TYPE_CONTENT_LIST)
1853       {
1854         struct Content *content      = (struct Content *)(conf[i].value);
1855         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1856         int c, x, y;
1857
1858         for (c = 0; c < max_num_entities; c++)
1859           for (y = 0; y < 3; y++)
1860             for (x = 0; x < 3; x++)
1861               content_copy[c].e[x][y] = content[c].e[x][y];
1862       }
1863     }
1864     else        // constant size configuration data (1, 2 or 4 bytes)
1865     {
1866       if (data_type == TYPE_BOOLEAN)
1867         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1868       else
1869         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
1870     }
1871   }
1872 }
1873
1874 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1875 {
1876   int i;
1877
1878   xx_ei = *ei_from;     // copy element data into temporary buffer
1879   yy_ei = *ei_to;       // copy element data into temporary buffer
1880
1881   copyConfigFromConfigList(chunk_config_CUSX_base);
1882
1883   *ei_from = xx_ei;
1884   *ei_to   = yy_ei;
1885
1886   // ---------- reinitialize and copy change pages ----------
1887
1888   ei_to->num_change_pages = ei_from->num_change_pages;
1889   ei_to->current_change_page = ei_from->current_change_page;
1890
1891   setElementChangePages(ei_to, ei_to->num_change_pages);
1892
1893   for (i = 0; i < ei_to->num_change_pages; i++)
1894     ei_to->change_page[i] = ei_from->change_page[i];
1895
1896   // ---------- copy group element info ----------
1897   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
1898     *ei_to->group = *ei_from->group;
1899
1900   // mark this custom element as modified
1901   ei_to->modified_settings = TRUE;
1902 }
1903
1904 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1905 {
1906   int change_page_size = sizeof(struct ElementChangeInfo);
1907
1908   ei->num_change_pages = MAX(1, change_pages);
1909
1910   ei->change_page =
1911     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1912
1913   if (ei->current_change_page >= ei->num_change_pages)
1914     ei->current_change_page = ei->num_change_pages - 1;
1915
1916   ei->change = &ei->change_page[ei->current_change_page];
1917 }
1918
1919 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1920 {
1921   xx_change = *change;          // copy change data into temporary buffer
1922
1923   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1924
1925   *change = xx_change;
1926
1927   resetEventFlags(change);
1928
1929   change->direct_action = 0;
1930   change->other_action = 0;
1931
1932   change->pre_change_function = NULL;
1933   change->change_function = NULL;
1934   change->post_change_function = NULL;
1935 }
1936
1937 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1938 {
1939   int i, x, y;
1940
1941   li = *level;          // copy level data into temporary buffer
1942   setConfigToDefaultsFromConfigList(chunk_config_INFO);
1943   *level = li;          // copy temporary buffer back to level data
1944
1945   setLevelInfoToDefaults_BD();
1946   setLevelInfoToDefaults_EM();
1947   setLevelInfoToDefaults_SP();
1948   setLevelInfoToDefaults_MM();
1949
1950   level->native_bd_level = &native_bd_level;
1951   level->native_em_level = &native_em_level;
1952   level->native_sp_level = &native_sp_level;
1953   level->native_mm_level = &native_mm_level;
1954
1955   level->file_version = FILE_VERSION_ACTUAL;
1956   level->game_version = GAME_VERSION_ACTUAL;
1957
1958   level->creation_date = getCurrentDate();
1959
1960   level->encoding_16bit_field  = TRUE;
1961   level->encoding_16bit_yamyam = TRUE;
1962   level->encoding_16bit_amoeba = TRUE;
1963
1964   // clear level name and level author string buffers
1965   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1966     level->name[i] = '\0';
1967   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1968     level->author[i] = '\0';
1969
1970   // set level name and level author to default values
1971   strcpy(level->name, NAMELESS_LEVEL_NAME);
1972   strcpy(level->author, ANONYMOUS_NAME);
1973
1974   // set level playfield to playable default level with player and exit
1975   for (x = 0; x < MAX_LEV_FIELDX; x++)
1976     for (y = 0; y < MAX_LEV_FIELDY; y++)
1977       level->field[x][y] = EL_SAND;
1978
1979   level->field[0][0] = EL_PLAYER_1;
1980   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1981
1982   BorderElement = EL_STEELWALL;
1983
1984   // detect custom elements when loading them
1985   level->file_has_custom_elements = FALSE;
1986
1987   // set all bug compatibility flags to "false" => do not emulate this bug
1988   level->use_action_after_change_bug = FALSE;
1989
1990   if (leveldir_current)
1991   {
1992     // try to determine better author name than 'anonymous'
1993     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1994     {
1995       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1996       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1997     }
1998     else
1999     {
2000       switch (LEVELCLASS(leveldir_current))
2001       {
2002         case LEVELCLASS_TUTORIAL:
2003           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2004           break;
2005
2006         case LEVELCLASS_CONTRIB:
2007           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2008           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2009           break;
2010
2011         case LEVELCLASS_PRIVATE:
2012           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2013           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2014           break;
2015
2016         default:
2017           // keep default value
2018           break;
2019       }
2020     }
2021   }
2022 }
2023
2024 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2025 {
2026   static boolean clipboard_elements_initialized = FALSE;
2027   int i;
2028
2029   InitElementPropertiesStatic();
2030
2031   li = *level;          // copy level data into temporary buffer
2032   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2033   *level = li;          // copy temporary buffer back to level data
2034
2035   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2036   {
2037     int element = i;
2038     struct ElementInfo *ei = &element_info[element];
2039
2040     if (element == EL_MM_GRAY_BALL)
2041     {
2042       struct LevelInfo_MM *level_mm = level->native_mm_level;
2043       int j;
2044
2045       for (j = 0; j < level->num_mm_ball_contents; j++)
2046         level->mm_ball_content[j] =
2047           map_element_MM_to_RND(level_mm->ball_content[j]);
2048     }
2049
2050     // never initialize clipboard elements after the very first time
2051     // (to be able to use clipboard elements between several levels)
2052     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2053       continue;
2054
2055     if (IS_ENVELOPE(element))
2056     {
2057       int envelope_nr = element - EL_ENVELOPE_1;
2058
2059       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2060
2061       level->envelope[envelope_nr] = xx_envelope;
2062     }
2063
2064     if (IS_CUSTOM_ELEMENT(element) ||
2065         IS_GROUP_ELEMENT(element) ||
2066         IS_INTERNAL_ELEMENT(element))
2067     {
2068       xx_ei = *ei;      // copy element data into temporary buffer
2069
2070       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2071
2072       *ei = xx_ei;
2073     }
2074
2075     setElementChangePages(ei, 1);
2076     setElementChangeInfoToDefaults(ei->change);
2077
2078     if (IS_CUSTOM_ELEMENT(element) ||
2079         IS_GROUP_ELEMENT(element))
2080     {
2081       setElementDescriptionToDefault(ei);
2082
2083       ei->modified_settings = FALSE;
2084     }
2085
2086     if (IS_CUSTOM_ELEMENT(element) ||
2087         IS_INTERNAL_ELEMENT(element))
2088     {
2089       // internal values used in level editor
2090
2091       ei->access_type = 0;
2092       ei->access_layer = 0;
2093       ei->access_protected = 0;
2094       ei->walk_to_action = 0;
2095       ei->smash_targets = 0;
2096       ei->deadliness = 0;
2097
2098       ei->can_explode_by_fire = FALSE;
2099       ei->can_explode_smashed = FALSE;
2100       ei->can_explode_impact = FALSE;
2101
2102       ei->current_change_page = 0;
2103     }
2104
2105     if (IS_GROUP_ELEMENT(element) ||
2106         IS_INTERNAL_ELEMENT(element))
2107     {
2108       struct ElementGroupInfo *group;
2109
2110       // initialize memory for list of elements in group
2111       if (ei->group == NULL)
2112         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2113
2114       group = ei->group;
2115
2116       xx_group = *group;        // copy group data into temporary buffer
2117
2118       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2119
2120       *group = xx_group;
2121     }
2122
2123     if (IS_EMPTY_ELEMENT(element) ||
2124         IS_INTERNAL_ELEMENT(element))
2125     {
2126       xx_ei = *ei;              // copy element data into temporary buffer
2127
2128       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2129
2130       *ei = xx_ei;
2131     }
2132   }
2133
2134   clipboard_elements_initialized = TRUE;
2135 }
2136
2137 static void setLevelInfoToDefaults(struct LevelInfo *level,
2138                                    boolean level_info_only,
2139                                    boolean reset_file_status)
2140 {
2141   setLevelInfoToDefaults_Level(level);
2142
2143   if (!level_info_only)
2144     setLevelInfoToDefaults_Elements(level);
2145
2146   if (reset_file_status)
2147   {
2148     level->no_valid_file = FALSE;
2149     level->no_level_file = FALSE;
2150   }
2151
2152   level->changed = FALSE;
2153 }
2154
2155 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2156 {
2157   level_file_info->nr = 0;
2158   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2159   level_file_info->packed = FALSE;
2160
2161   setString(&level_file_info->basename, NULL);
2162   setString(&level_file_info->filename, NULL);
2163 }
2164
2165 int getMappedElement_SB(int, boolean);
2166
2167 static void ActivateLevelTemplate(void)
2168 {
2169   int x, y;
2170
2171   if (check_special_flags("load_xsb_to_ces"))
2172   {
2173     // fill smaller playfields with padding "beyond border wall" elements
2174     if (level.fieldx < level_template.fieldx ||
2175         level.fieldy < level_template.fieldy)
2176     {
2177       short field[level.fieldx][level.fieldy];
2178       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2179       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2180       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2181       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2182
2183       // copy old playfield (which is smaller than the visible area)
2184       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2185         field[x][y] = level.field[x][y];
2186
2187       // fill new, larger playfield with "beyond border wall" elements
2188       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2189         level.field[x][y] = getMappedElement_SB('_', TRUE);
2190
2191       // copy the old playfield to the middle of the new playfield
2192       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2193         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2194
2195       level.fieldx = new_fieldx;
2196       level.fieldy = new_fieldy;
2197     }
2198   }
2199
2200   // Currently there is no special action needed to activate the template
2201   // data, because 'element_info' property settings overwrite the original
2202   // level data, while all other variables do not change.
2203
2204   // Exception: 'from_level_template' elements in the original level playfield
2205   // are overwritten with the corresponding elements at the same position in
2206   // playfield from the level template.
2207
2208   for (x = 0; x < level.fieldx; x++)
2209     for (y = 0; y < level.fieldy; y++)
2210       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2211         level.field[x][y] = level_template.field[x][y];
2212
2213   if (check_special_flags("load_xsb_to_ces"))
2214   {
2215     struct LevelInfo level_backup = level;
2216
2217     // overwrite all individual level settings from template level settings
2218     level = level_template;
2219
2220     // restore level file info
2221     level.file_info = level_backup.file_info;
2222
2223     // restore playfield size
2224     level.fieldx = level_backup.fieldx;
2225     level.fieldy = level_backup.fieldy;
2226
2227     // restore playfield content
2228     for (x = 0; x < level.fieldx; x++)
2229       for (y = 0; y < level.fieldy; y++)
2230         level.field[x][y] = level_backup.field[x][y];
2231
2232     // restore name and author from individual level
2233     strcpy(level.name,   level_backup.name);
2234     strcpy(level.author, level_backup.author);
2235
2236     // restore flag "use_custom_template"
2237     level.use_custom_template = level_backup.use_custom_template;
2238   }
2239 }
2240
2241 static boolean checkForPackageFromBasename_BD(char *basename)
2242 {
2243   // check for native BD level file extensions
2244   if (!strSuffixLower(basename, ".bd") &&
2245       !strSuffixLower(basename, ".bdr") &&
2246       !strSuffixLower(basename, ".brc") &&
2247       !strSuffixLower(basename, ".gds"))
2248     return FALSE;
2249
2250   // check for standard single-level BD files (like "001.bd")
2251   if (strSuffixLower(basename, ".bd") &&
2252       strlen(basename) == 6 &&
2253       basename[0] >= '0' && basename[0] <= '9' &&
2254       basename[1] >= '0' && basename[1] <= '9' &&
2255       basename[2] >= '0' && basename[2] <= '9')
2256     return FALSE;
2257
2258   // this is a level package in native BD file format
2259   return TRUE;
2260 }
2261
2262 static char *getLevelFilenameFromBasename(char *basename)
2263 {
2264   static char *filename = NULL;
2265
2266   checked_free(filename);
2267
2268   filename = getPath2(getCurrentLevelDir(), basename);
2269
2270   return filename;
2271 }
2272
2273 static int getFileTypeFromBasename(char *basename)
2274 {
2275   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2276
2277   static char *filename = NULL;
2278   struct stat file_status;
2279
2280   // ---------- try to determine file type from filename ----------
2281
2282   // check for typical filename of a Supaplex level package file
2283   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2284     return LEVEL_FILE_TYPE_SP;
2285
2286   // check for typical filename of a Diamond Caves II level package file
2287   if (strSuffixLower(basename, ".dc") ||
2288       strSuffixLower(basename, ".dc2"))
2289     return LEVEL_FILE_TYPE_DC;
2290
2291   // check for typical filename of a Sokoban level package file
2292   if (strSuffixLower(basename, ".xsb") &&
2293       strchr(basename, '%') == NULL)
2294     return LEVEL_FILE_TYPE_SB;
2295
2296   // check for typical filename of a Boulder Dash (GDash) level package file
2297   if (checkForPackageFromBasename_BD(basename))
2298     return LEVEL_FILE_TYPE_BD;
2299
2300   // ---------- try to determine file type from filesize ----------
2301
2302   checked_free(filename);
2303   filename = getPath2(getCurrentLevelDir(), basename);
2304
2305   if (stat(filename, &file_status) == 0)
2306   {
2307     // check for typical filesize of a Supaplex level package file
2308     if (file_status.st_size == 170496)
2309       return LEVEL_FILE_TYPE_SP;
2310   }
2311
2312   return LEVEL_FILE_TYPE_UNKNOWN;
2313 }
2314
2315 static int getFileTypeFromMagicBytes(char *filename, int type)
2316 {
2317   File *file;
2318
2319   if ((file = openFile(filename, MODE_READ)))
2320   {
2321     char chunk_name[CHUNK_ID_LEN + 1];
2322
2323     getFileChunkBE(file, chunk_name, NULL);
2324
2325     if (strEqual(chunk_name, "MMII") ||
2326         strEqual(chunk_name, "MIRR"))
2327       type = LEVEL_FILE_TYPE_MM;
2328
2329     closeFile(file);
2330   }
2331
2332   return type;
2333 }
2334
2335 static boolean checkForPackageFromBasename(char *basename)
2336 {
2337   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2338   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2339
2340   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2341 }
2342
2343 static char *getSingleLevelBasenameExt(int nr, char *extension)
2344 {
2345   static char basename[MAX_FILENAME_LEN];
2346
2347   if (nr < 0)
2348     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2349   else
2350     sprintf(basename, "%03d.%s", nr, extension);
2351
2352   return basename;
2353 }
2354
2355 static char *getSingleLevelBasename(int nr)
2356 {
2357   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2358 }
2359
2360 static char *getPackedLevelBasename(int type)
2361 {
2362   static char basename[MAX_FILENAME_LEN];
2363   char *directory = getCurrentLevelDir();
2364   Directory *dir;
2365   DirectoryEntry *dir_entry;
2366
2367   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2368
2369   if ((dir = openDirectory(directory)) == NULL)
2370   {
2371     Warn("cannot read current level directory '%s'", directory);
2372
2373     return basename;
2374   }
2375
2376   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2377   {
2378     char *entry_basename = dir_entry->basename;
2379     int entry_type = getFileTypeFromBasename(entry_basename);
2380
2381     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2382     {
2383       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2384           type == entry_type)
2385       {
2386         strcpy(basename, entry_basename);
2387
2388         break;
2389       }
2390     }
2391   }
2392
2393   closeDirectory(dir);
2394
2395   return basename;
2396 }
2397
2398 static char *getSingleLevelFilename(int nr)
2399 {
2400   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2401 }
2402
2403 #if ENABLE_UNUSED_CODE
2404 static char *getPackedLevelFilename(int type)
2405 {
2406   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2407 }
2408 #endif
2409
2410 char *getDefaultLevelFilename(int nr)
2411 {
2412   return getSingleLevelFilename(nr);
2413 }
2414
2415 #if ENABLE_UNUSED_CODE
2416 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2417                                                  int type)
2418 {
2419   lfi->type = type;
2420   lfi->packed = FALSE;
2421
2422   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2423   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2424 }
2425 #endif
2426
2427 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2428                                                  int type, char *format, ...)
2429 {
2430   static char basename[MAX_FILENAME_LEN];
2431   va_list ap;
2432
2433   va_start(ap, format);
2434   vsprintf(basename, format, ap);
2435   va_end(ap);
2436
2437   lfi->type = type;
2438   lfi->packed = FALSE;
2439
2440   setString(&lfi->basename, basename);
2441   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2442 }
2443
2444 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2445                                                  int type)
2446 {
2447   lfi->type = type;
2448   lfi->packed = TRUE;
2449
2450   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2451   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2452 }
2453
2454 static int getFiletypeFromID(char *filetype_id)
2455 {
2456   char *filetype_id_lower;
2457   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2458   int i;
2459
2460   if (filetype_id == NULL)
2461     return LEVEL_FILE_TYPE_UNKNOWN;
2462
2463   filetype_id_lower = getStringToLower(filetype_id);
2464
2465   for (i = 0; filetype_id_list[i].id != NULL; i++)
2466   {
2467     char *id_lower = getStringToLower(filetype_id_list[i].id);
2468     
2469     if (strEqual(filetype_id_lower, id_lower))
2470       filetype = filetype_id_list[i].filetype;
2471
2472     free(id_lower);
2473
2474     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2475       break;
2476   }
2477
2478   free(filetype_id_lower);
2479
2480   return filetype;
2481 }
2482
2483 char *getLocalLevelTemplateFilename(void)
2484 {
2485   return getDefaultLevelFilename(-1);
2486 }
2487
2488 char *getGlobalLevelTemplateFilename(void)
2489 {
2490   // global variable "leveldir_current" must be modified in the loop below
2491   LevelDirTree *leveldir_current_last = leveldir_current;
2492   char *filename = NULL;
2493
2494   // check for template level in path from current to topmost tree node
2495
2496   while (leveldir_current != NULL)
2497   {
2498     filename = getDefaultLevelFilename(-1);
2499
2500     if (fileExists(filename))
2501       break;
2502
2503     leveldir_current = leveldir_current->node_parent;
2504   }
2505
2506   // restore global variable "leveldir_current" modified in above loop
2507   leveldir_current = leveldir_current_last;
2508
2509   return filename;
2510 }
2511
2512 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2513 {
2514   int nr = lfi->nr;
2515
2516   // special case: level number is negative => check for level template file
2517   if (nr < 0)
2518   {
2519     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2520                                          getSingleLevelBasename(-1));
2521
2522     // replace local level template filename with global template filename
2523     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2524
2525     // no fallback if template file not existing
2526     return;
2527   }
2528
2529   // special case: check for file name/pattern specified in "levelinfo.conf"
2530   if (leveldir_current->level_filename != NULL)
2531   {
2532     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2533
2534     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2535                                          leveldir_current->level_filename, nr);
2536
2537     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2538
2539     if (fileExists(lfi->filename))
2540       return;
2541   }
2542   else if (leveldir_current->level_filetype != NULL)
2543   {
2544     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2545
2546     // check for specified native level file with standard file name
2547     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2548                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2549     if (fileExists(lfi->filename))
2550       return;
2551   }
2552
2553   // check for native Rocks'n'Diamonds level file
2554   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2555                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2556   if (fileExists(lfi->filename))
2557     return;
2558
2559   // check for native Boulder Dash level file
2560   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2561   if (fileExists(lfi->filename))
2562     return;
2563
2564   // check for Emerald Mine level file (V1)
2565   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2566                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2567   if (fileExists(lfi->filename))
2568     return;
2569   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2570                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2571   if (fileExists(lfi->filename))
2572     return;
2573
2574   // check for Emerald Mine level file (V2 to V5)
2575   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2576   if (fileExists(lfi->filename))
2577     return;
2578
2579   // check for Emerald Mine level file (V6 / single mode)
2580   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2581   if (fileExists(lfi->filename))
2582     return;
2583   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2584   if (fileExists(lfi->filename))
2585     return;
2586
2587   // check for Emerald Mine level file (V6 / teamwork mode)
2588   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2589   if (fileExists(lfi->filename))
2590     return;
2591   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2592   if (fileExists(lfi->filename))
2593     return;
2594
2595   // check for various packed level file formats
2596   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2597   if (fileExists(lfi->filename))
2598     return;
2599
2600   // no known level file found -- use default values (and fail later)
2601   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2602                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2603 }
2604
2605 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2606 {
2607   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2608     lfi->type = getFileTypeFromBasename(lfi->basename);
2609
2610   if (lfi->type == LEVEL_FILE_TYPE_RND)
2611     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2612 }
2613
2614 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2615 {
2616   // always start with reliable default values
2617   setFileInfoToDefaults(level_file_info);
2618
2619   level_file_info->nr = nr;     // set requested level number
2620
2621   determineLevelFileInfo_Filename(level_file_info);
2622   determineLevelFileInfo_Filetype(level_file_info);
2623 }
2624
2625 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2626                               struct LevelFileInfo *lfi_to)
2627 {
2628   lfi_to->nr     = lfi_from->nr;
2629   lfi_to->type   = lfi_from->type;
2630   lfi_to->packed = lfi_from->packed;
2631
2632   setString(&lfi_to->basename, lfi_from->basename);
2633   setString(&lfi_to->filename, lfi_from->filename);
2634 }
2635
2636 // ----------------------------------------------------------------------------
2637 // functions for loading R'n'D level
2638 // ----------------------------------------------------------------------------
2639
2640 int getMappedElement(int element)
2641 {
2642   // remap some (historic, now obsolete) elements
2643
2644   switch (element)
2645   {
2646     case EL_PLAYER_OBSOLETE:
2647       element = EL_PLAYER_1;
2648       break;
2649
2650     case EL_KEY_OBSOLETE:
2651       element = EL_KEY_1;
2652       break;
2653
2654     case EL_EM_KEY_1_FILE_OBSOLETE:
2655       element = EL_EM_KEY_1;
2656       break;
2657
2658     case EL_EM_KEY_2_FILE_OBSOLETE:
2659       element = EL_EM_KEY_2;
2660       break;
2661
2662     case EL_EM_KEY_3_FILE_OBSOLETE:
2663       element = EL_EM_KEY_3;
2664       break;
2665
2666     case EL_EM_KEY_4_FILE_OBSOLETE:
2667       element = EL_EM_KEY_4;
2668       break;
2669
2670     case EL_ENVELOPE_OBSOLETE:
2671       element = EL_ENVELOPE_1;
2672       break;
2673
2674     case EL_SP_EMPTY:
2675       element = EL_EMPTY;
2676       break;
2677
2678     default:
2679       if (element >= NUM_FILE_ELEMENTS)
2680       {
2681         Warn("invalid level element %d", element);
2682
2683         element = EL_UNKNOWN;
2684       }
2685       break;
2686   }
2687
2688   return element;
2689 }
2690
2691 static int getMappedElementByVersion(int element, int game_version)
2692 {
2693   // remap some elements due to certain game version
2694
2695   if (game_version <= VERSION_IDENT(2,2,0,0))
2696   {
2697     // map game font elements
2698     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2699                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2700                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2701                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2702   }
2703
2704   if (game_version < VERSION_IDENT(3,0,0,0))
2705   {
2706     // map Supaplex gravity tube elements
2707     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2708                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2709                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2710                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2711                element);
2712   }
2713
2714   return element;
2715 }
2716
2717 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2718 {
2719   level->file_version = getFileVersion(file);
2720   level->game_version = getFileVersion(file);
2721
2722   return chunk_size;
2723 }
2724
2725 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2726 {
2727   level->creation_date.year  = getFile16BitBE(file);
2728   level->creation_date.month = getFile8Bit(file);
2729   level->creation_date.day   = getFile8Bit(file);
2730
2731   level->creation_date.src   = DATE_SRC_LEVELFILE;
2732
2733   return chunk_size;
2734 }
2735
2736 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2737 {
2738   int initial_player_stepsize;
2739   int initial_player_gravity;
2740   int i, x, y;
2741
2742   level->fieldx = getFile8Bit(file);
2743   level->fieldy = getFile8Bit(file);
2744
2745   level->time           = getFile16BitBE(file);
2746   level->gems_needed    = getFile16BitBE(file);
2747
2748   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2749     level->name[i] = getFile8Bit(file);
2750   level->name[MAX_LEVEL_NAME_LEN] = 0;
2751
2752   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2753     level->score[i] = getFile8Bit(file);
2754
2755   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2756   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2757     for (y = 0; y < 3; y++)
2758       for (x = 0; x < 3; x++)
2759         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2760
2761   level->amoeba_speed           = getFile8Bit(file);
2762   level->time_magic_wall        = getFile8Bit(file);
2763   level->time_wheel             = getFile8Bit(file);
2764   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2765
2766   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2767                                    STEPSIZE_NORMAL);
2768
2769   for (i = 0; i < MAX_PLAYERS; i++)
2770     level->initial_player_stepsize[i] = initial_player_stepsize;
2771
2772   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2773
2774   for (i = 0; i < MAX_PLAYERS; i++)
2775     level->initial_player_gravity[i] = initial_player_gravity;
2776
2777   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2778   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2779
2780   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2781
2782   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2783   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2784   level->can_move_into_acid_bits = getFile32BitBE(file);
2785   level->dont_collide_with_bits = getFile8Bit(file);
2786
2787   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2788   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2789
2790   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2791   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2792   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2793
2794   level->game_engine_type       = getFile8Bit(file);
2795
2796   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2797
2798   return chunk_size;
2799 }
2800
2801 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2802 {
2803   int i;
2804
2805   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2806     level->name[i] = getFile8Bit(file);
2807   level->name[MAX_LEVEL_NAME_LEN] = 0;
2808
2809   return chunk_size;
2810 }
2811
2812 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2813 {
2814   int i;
2815
2816   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2817     level->author[i] = getFile8Bit(file);
2818   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2819
2820   return chunk_size;
2821 }
2822
2823 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2824 {
2825   int x, y;
2826   int chunk_size_expected = level->fieldx * level->fieldy;
2827
2828   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2829      stored with 16-bit encoding (and should be twice as big then).
2830      Even worse, playfield data was stored 16-bit when only yamyam content
2831      contained 16-bit elements and vice versa. */
2832
2833   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2834     chunk_size_expected *= 2;
2835
2836   if (chunk_size_expected != chunk_size)
2837   {
2838     ReadUnusedBytesFromFile(file, chunk_size);
2839     return chunk_size_expected;
2840   }
2841
2842   for (y = 0; y < level->fieldy; y++)
2843     for (x = 0; x < level->fieldx; x++)
2844       level->field[x][y] =
2845         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2846                          getFile8Bit(file));
2847   return chunk_size;
2848 }
2849
2850 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2851 {
2852   int i, x, y;
2853   int header_size = 4;
2854   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2855   int chunk_size_expected = header_size + content_size;
2856
2857   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2858      stored with 16-bit encoding (and should be twice as big then).
2859      Even worse, playfield data was stored 16-bit when only yamyam content
2860      contained 16-bit elements and vice versa. */
2861
2862   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2863     chunk_size_expected += content_size;
2864
2865   if (chunk_size_expected != chunk_size)
2866   {
2867     ReadUnusedBytesFromFile(file, chunk_size);
2868     return chunk_size_expected;
2869   }
2870
2871   getFile8Bit(file);
2872   level->num_yamyam_contents = getFile8Bit(file);
2873   getFile8Bit(file);
2874   getFile8Bit(file);
2875
2876   // correct invalid number of content fields -- should never happen
2877   if (level->num_yamyam_contents < 1 ||
2878       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2879     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2880
2881   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2882     for (y = 0; y < 3; y++)
2883       for (x = 0; x < 3; x++)
2884         level->yamyam_content[i].e[x][y] =
2885           getMappedElement(level->encoding_16bit_field ?
2886                            getFile16BitBE(file) : getFile8Bit(file));
2887   return chunk_size;
2888 }
2889
2890 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2891 {
2892   int i, x, y;
2893   int element;
2894   int num_contents;
2895   int content_array[MAX_ELEMENT_CONTENTS][3][3];
2896
2897   element = getMappedElement(getFile16BitBE(file));
2898   num_contents = getFile8Bit(file);
2899
2900   getFile8Bit(file);    // content x size (unused)
2901   getFile8Bit(file);    // content y size (unused)
2902
2903   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2904
2905   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2906     for (y = 0; y < 3; y++)
2907       for (x = 0; x < 3; x++)
2908         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2909
2910   // correct invalid number of content fields -- should never happen
2911   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2912     num_contents = STD_ELEMENT_CONTENTS;
2913
2914   if (element == EL_YAMYAM)
2915   {
2916     level->num_yamyam_contents = num_contents;
2917
2918     for (i = 0; i < num_contents; i++)
2919       for (y = 0; y < 3; y++)
2920         for (x = 0; x < 3; x++)
2921           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2922   }
2923   else if (element == EL_BD_AMOEBA)
2924   {
2925     level->amoeba_content = content_array[0][0][0];
2926   }
2927   else
2928   {
2929     Warn("cannot load content for element '%d'", element);
2930   }
2931
2932   return chunk_size;
2933 }
2934
2935 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2936 {
2937   int i;
2938   int element;
2939   int envelope_nr;
2940   int envelope_len;
2941   int chunk_size_expected;
2942
2943   element = getMappedElement(getFile16BitBE(file));
2944   if (!IS_ENVELOPE(element))
2945     element = EL_ENVELOPE_1;
2946
2947   envelope_nr = element - EL_ENVELOPE_1;
2948
2949   envelope_len = getFile16BitBE(file);
2950
2951   level->envelope[envelope_nr].xsize = getFile8Bit(file);
2952   level->envelope[envelope_nr].ysize = getFile8Bit(file);
2953
2954   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2955
2956   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2957   if (chunk_size_expected != chunk_size)
2958   {
2959     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2960     return chunk_size_expected;
2961   }
2962
2963   for (i = 0; i < envelope_len; i++)
2964     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2965
2966   return chunk_size;
2967 }
2968
2969 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2970 {
2971   int num_changed_custom_elements = getFile16BitBE(file);
2972   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2973   int i;
2974
2975   if (chunk_size_expected != chunk_size)
2976   {
2977     ReadUnusedBytesFromFile(file, chunk_size - 2);
2978     return chunk_size_expected;
2979   }
2980
2981   for (i = 0; i < num_changed_custom_elements; i++)
2982   {
2983     int element = getMappedElement(getFile16BitBE(file));
2984     int properties = getFile32BitBE(file);
2985
2986     if (IS_CUSTOM_ELEMENT(element))
2987       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2988     else
2989       Warn("invalid custom element number %d", element);
2990
2991     // older game versions that wrote level files with CUS1 chunks used
2992     // different default push delay values (not yet stored in level file)
2993     element_info[element].push_delay_fixed = 2;
2994     element_info[element].push_delay_random = 8;
2995   }
2996
2997   level->file_has_custom_elements = TRUE;
2998
2999   return chunk_size;
3000 }
3001
3002 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3003 {
3004   int num_changed_custom_elements = getFile16BitBE(file);
3005   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3006   int i;
3007
3008   if (chunk_size_expected != chunk_size)
3009   {
3010     ReadUnusedBytesFromFile(file, chunk_size - 2);
3011     return chunk_size_expected;
3012   }
3013
3014   for (i = 0; i < num_changed_custom_elements; i++)
3015   {
3016     int element = getMappedElement(getFile16BitBE(file));
3017     int custom_target_element = getMappedElement(getFile16BitBE(file));
3018
3019     if (IS_CUSTOM_ELEMENT(element))
3020       element_info[element].change->target_element = custom_target_element;
3021     else
3022       Warn("invalid custom element number %d", element);
3023   }
3024
3025   level->file_has_custom_elements = TRUE;
3026
3027   return chunk_size;
3028 }
3029
3030 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3031 {
3032   int num_changed_custom_elements = getFile16BitBE(file);
3033   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3034   int i, j, x, y;
3035
3036   if (chunk_size_expected != chunk_size)
3037   {
3038     ReadUnusedBytesFromFile(file, chunk_size - 2);
3039     return chunk_size_expected;
3040   }
3041
3042   for (i = 0; i < num_changed_custom_elements; i++)
3043   {
3044     int element = getMappedElement(getFile16BitBE(file));
3045     struct ElementInfo *ei = &element_info[element];
3046     unsigned int event_bits;
3047
3048     if (!IS_CUSTOM_ELEMENT(element))
3049     {
3050       Warn("invalid custom element number %d", element);
3051
3052       element = EL_INTERNAL_DUMMY;
3053     }
3054
3055     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3056       ei->description[j] = getFile8Bit(file);
3057     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3058
3059     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3060
3061     // some free bytes for future properties and padding
3062     ReadUnusedBytesFromFile(file, 7);
3063
3064     ei->use_gfx_element = getFile8Bit(file);
3065     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3066
3067     ei->collect_score_initial = getFile8Bit(file);
3068     ei->collect_count_initial = getFile8Bit(file);
3069
3070     ei->push_delay_fixed = getFile16BitBE(file);
3071     ei->push_delay_random = getFile16BitBE(file);
3072     ei->move_delay_fixed = getFile16BitBE(file);
3073     ei->move_delay_random = getFile16BitBE(file);
3074
3075     ei->move_pattern = getFile16BitBE(file);
3076     ei->move_direction_initial = getFile8Bit(file);
3077     ei->move_stepsize = getFile8Bit(file);
3078
3079     for (y = 0; y < 3; y++)
3080       for (x = 0; x < 3; x++)
3081         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3082
3083     // bits 0 - 31 of "has_event[]"
3084     event_bits = getFile32BitBE(file);
3085     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3086       if (event_bits & (1u << j))
3087         ei->change->has_event[j] = TRUE;
3088
3089     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3090
3091     ei->change->delay_fixed = getFile16BitBE(file);
3092     ei->change->delay_random = getFile16BitBE(file);
3093     ei->change->delay_frames = getFile16BitBE(file);
3094
3095     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3096
3097     ei->change->explode = getFile8Bit(file);
3098     ei->change->use_target_content = getFile8Bit(file);
3099     ei->change->only_if_complete = getFile8Bit(file);
3100     ei->change->use_random_replace = getFile8Bit(file);
3101
3102     ei->change->random_percentage = getFile8Bit(file);
3103     ei->change->replace_when = getFile8Bit(file);
3104
3105     for (y = 0; y < 3; y++)
3106       for (x = 0; x < 3; x++)
3107         ei->change->target_content.e[x][y] =
3108           getMappedElement(getFile16BitBE(file));
3109
3110     ei->slippery_type = getFile8Bit(file);
3111
3112     // some free bytes for future properties and padding
3113     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3114
3115     // mark that this custom element has been modified
3116     ei->modified_settings = TRUE;
3117   }
3118
3119   level->file_has_custom_elements = TRUE;
3120
3121   return chunk_size;
3122 }
3123
3124 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3125 {
3126   struct ElementInfo *ei;
3127   int chunk_size_expected;
3128   int element;
3129   int i, j, x, y;
3130
3131   // ---------- custom element base property values (96 bytes) ----------------
3132
3133   element = getMappedElement(getFile16BitBE(file));
3134
3135   if (!IS_CUSTOM_ELEMENT(element))
3136   {
3137     Warn("invalid custom element number %d", element);
3138
3139     ReadUnusedBytesFromFile(file, chunk_size - 2);
3140
3141     return chunk_size;
3142   }
3143
3144   ei = &element_info[element];
3145
3146   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3147     ei->description[i] = getFile8Bit(file);
3148   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3149
3150   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3151
3152   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3153
3154   ei->num_change_pages = getFile8Bit(file);
3155
3156   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3157   if (chunk_size_expected != chunk_size)
3158   {
3159     ReadUnusedBytesFromFile(file, chunk_size - 43);
3160     return chunk_size_expected;
3161   }
3162
3163   ei->ce_value_fixed_initial = getFile16BitBE(file);
3164   ei->ce_value_random_initial = getFile16BitBE(file);
3165   ei->use_last_ce_value = getFile8Bit(file);
3166
3167   ei->use_gfx_element = getFile8Bit(file);
3168   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3169
3170   ei->collect_score_initial = getFile8Bit(file);
3171   ei->collect_count_initial = getFile8Bit(file);
3172
3173   ei->drop_delay_fixed = getFile8Bit(file);
3174   ei->push_delay_fixed = getFile8Bit(file);
3175   ei->drop_delay_random = getFile8Bit(file);
3176   ei->push_delay_random = getFile8Bit(file);
3177   ei->move_delay_fixed = getFile16BitBE(file);
3178   ei->move_delay_random = getFile16BitBE(file);
3179
3180   // bits 0 - 15 of "move_pattern" ...
3181   ei->move_pattern = getFile16BitBE(file);
3182   ei->move_direction_initial = getFile8Bit(file);
3183   ei->move_stepsize = getFile8Bit(file);
3184
3185   ei->slippery_type = getFile8Bit(file);
3186
3187   for (y = 0; y < 3; y++)
3188     for (x = 0; x < 3; x++)
3189       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3190
3191   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3192   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3193   ei->move_leave_type = getFile8Bit(file);
3194
3195   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3196   ei->move_pattern |= (getFile16BitBE(file) << 16);
3197
3198   ei->access_direction = getFile8Bit(file);
3199
3200   ei->explosion_delay = getFile8Bit(file);
3201   ei->ignition_delay = getFile8Bit(file);
3202   ei->explosion_type = getFile8Bit(file);
3203
3204   // some free bytes for future custom property values and padding
3205   ReadUnusedBytesFromFile(file, 1);
3206
3207   // ---------- change page property values (48 bytes) ------------------------
3208
3209   setElementChangePages(ei, ei->num_change_pages);
3210
3211   for (i = 0; i < ei->num_change_pages; i++)
3212   {
3213     struct ElementChangeInfo *change = &ei->change_page[i];
3214     unsigned int event_bits;
3215
3216     // always start with reliable default values
3217     setElementChangeInfoToDefaults(change);
3218
3219     // bits 0 - 31 of "has_event[]" ...
3220     event_bits = getFile32BitBE(file);
3221     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3222       if (event_bits & (1u << j))
3223         change->has_event[j] = TRUE;
3224
3225     change->target_element = getMappedElement(getFile16BitBE(file));
3226
3227     change->delay_fixed = getFile16BitBE(file);
3228     change->delay_random = getFile16BitBE(file);
3229     change->delay_frames = getFile16BitBE(file);
3230
3231     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3232
3233     change->explode = getFile8Bit(file);
3234     change->use_target_content = getFile8Bit(file);
3235     change->only_if_complete = getFile8Bit(file);
3236     change->use_random_replace = getFile8Bit(file);
3237
3238     change->random_percentage = getFile8Bit(file);
3239     change->replace_when = getFile8Bit(file);
3240
3241     for (y = 0; y < 3; y++)
3242       for (x = 0; x < 3; x++)
3243         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3244
3245     change->can_change = getFile8Bit(file);
3246
3247     change->trigger_side = getFile8Bit(file);
3248
3249     change->trigger_player = getFile8Bit(file);
3250     change->trigger_page = getFile8Bit(file);
3251
3252     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3253                             CH_PAGE_ANY : (1 << change->trigger_page));
3254
3255     change->has_action = getFile8Bit(file);
3256     change->action_type = getFile8Bit(file);
3257     change->action_mode = getFile8Bit(file);
3258     change->action_arg = getFile16BitBE(file);
3259
3260     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3261     event_bits = getFile8Bit(file);
3262     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3263       if (event_bits & (1u << (j - 32)))
3264         change->has_event[j] = TRUE;
3265   }
3266
3267   // mark this custom element as modified
3268   ei->modified_settings = TRUE;
3269
3270   level->file_has_custom_elements = TRUE;
3271
3272   return chunk_size;
3273 }
3274
3275 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3276 {
3277   struct ElementInfo *ei;
3278   struct ElementGroupInfo *group;
3279   int element;
3280   int i;
3281
3282   element = getMappedElement(getFile16BitBE(file));
3283
3284   if (!IS_GROUP_ELEMENT(element))
3285   {
3286     Warn("invalid group element number %d", element);
3287
3288     ReadUnusedBytesFromFile(file, chunk_size - 2);
3289
3290     return chunk_size;
3291   }
3292
3293   ei = &element_info[element];
3294
3295   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3296     ei->description[i] = getFile8Bit(file);
3297   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3298
3299   group = element_info[element].group;
3300
3301   group->num_elements = getFile8Bit(file);
3302
3303   ei->use_gfx_element = getFile8Bit(file);
3304   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3305
3306   group->choice_mode = getFile8Bit(file);
3307
3308   // some free bytes for future values and padding
3309   ReadUnusedBytesFromFile(file, 3);
3310
3311   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3312     group->element[i] = getMappedElement(getFile16BitBE(file));
3313
3314   // mark this group element as modified
3315   element_info[element].modified_settings = TRUE;
3316
3317   level->file_has_custom_elements = TRUE;
3318
3319   return chunk_size;
3320 }
3321
3322 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3323                                 int element, int real_element)
3324 {
3325   int micro_chunk_size = 0;
3326   int conf_type = getFile8Bit(file);
3327   int byte_mask = conf_type & CONF_MASK_BYTES;
3328   boolean element_found = FALSE;
3329   int i;
3330
3331   micro_chunk_size += 1;
3332
3333   if (byte_mask == CONF_MASK_MULTI_BYTES)
3334   {
3335     int num_bytes = getFile16BitBE(file);
3336     byte *buffer = checked_malloc(num_bytes);
3337
3338     ReadBytesFromFile(file, buffer, num_bytes);
3339
3340     for (i = 0; conf[i].data_type != -1; i++)
3341     {
3342       if (conf[i].element == element &&
3343           conf[i].conf_type == conf_type)
3344       {
3345         int data_type = conf[i].data_type;
3346         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3347         int max_num_entities = conf[i].max_num_entities;
3348
3349         if (num_entities > max_num_entities)
3350         {
3351           Warn("truncating number of entities for element %d from %d to %d",
3352                element, num_entities, max_num_entities);
3353
3354           num_entities = max_num_entities;
3355         }
3356
3357         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3358                                   data_type == TYPE_CONTENT_LIST))
3359         {
3360           // for element and content lists, zero entities are not allowed
3361           Warn("found empty list of entities for element %d", element);
3362
3363           // do not set "num_entities" here to prevent reading behind buffer
3364
3365           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3366         }
3367         else
3368         {
3369           *(int *)(conf[i].num_entities) = num_entities;
3370         }
3371
3372         element_found = TRUE;
3373
3374         if (data_type == TYPE_STRING)
3375         {
3376           char *string = (char *)(conf[i].value);
3377           int j;
3378
3379           for (j = 0; j < max_num_entities; j++)
3380             string[j] = (j < num_entities ? buffer[j] : '\0');
3381         }
3382         else if (data_type == TYPE_ELEMENT_LIST)
3383         {
3384           int *element_array = (int *)(conf[i].value);
3385           int j;
3386
3387           for (j = 0; j < num_entities; j++)
3388             element_array[j] =
3389               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3390         }
3391         else if (data_type == TYPE_CONTENT_LIST)
3392         {
3393           struct Content *content= (struct Content *)(conf[i].value);
3394           int c, x, y;
3395
3396           for (c = 0; c < num_entities; c++)
3397             for (y = 0; y < 3; y++)
3398               for (x = 0; x < 3; x++)
3399                 content[c].e[x][y] =
3400                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3401         }
3402         else
3403           element_found = FALSE;
3404
3405         break;
3406       }
3407     }
3408
3409     checked_free(buffer);
3410
3411     micro_chunk_size += 2 + num_bytes;
3412   }
3413   else          // constant size configuration data (1, 2 or 4 bytes)
3414   {
3415     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3416                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3417                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3418
3419     for (i = 0; conf[i].data_type != -1; i++)
3420     {
3421       if (conf[i].element == element &&
3422           conf[i].conf_type == conf_type)
3423       {
3424         int data_type = conf[i].data_type;
3425
3426         if (data_type == TYPE_ELEMENT)
3427           value = getMappedElement(value);
3428
3429         if (data_type == TYPE_BOOLEAN)
3430           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3431         else
3432           *(int *)    (conf[i].value) = value;
3433
3434         element_found = TRUE;
3435
3436         break;
3437       }
3438     }
3439
3440     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3441   }
3442
3443   if (!element_found)
3444   {
3445     char *error_conf_chunk_bytes =
3446       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3447        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3448        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3449     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3450     int error_element = real_element;
3451
3452     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3453          error_conf_chunk_bytes, error_conf_chunk_token,
3454          error_element, EL_NAME(error_element));
3455   }
3456
3457   return micro_chunk_size;
3458 }
3459
3460 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3461 {
3462   int real_chunk_size = 0;
3463
3464   li = *level;          // copy level data into temporary buffer
3465
3466   while (!checkEndOfFile(file))
3467   {
3468     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3469
3470     if (real_chunk_size >= chunk_size)
3471       break;
3472   }
3473
3474   *level = li;          // copy temporary buffer back to level data
3475
3476   return real_chunk_size;
3477 }
3478
3479 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3480 {
3481   int real_chunk_size = 0;
3482
3483   li = *level;          // copy level data into temporary buffer
3484
3485   while (!checkEndOfFile(file))
3486   {
3487     int element = getMappedElement(getFile16BitBE(file));
3488
3489     real_chunk_size += 2;
3490     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3491                                             element, element);
3492     if (real_chunk_size >= chunk_size)
3493       break;
3494   }
3495
3496   *level = li;          // copy temporary buffer back to level data
3497
3498   return real_chunk_size;
3499 }
3500
3501 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3502 {
3503   int real_chunk_size = 0;
3504
3505   li = *level;          // copy level data into temporary buffer
3506
3507   while (!checkEndOfFile(file))
3508   {
3509     int element = getMappedElement(getFile16BitBE(file));
3510
3511     real_chunk_size += 2;
3512     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3513                                             element, element);
3514     if (real_chunk_size >= chunk_size)
3515       break;
3516   }
3517
3518   *level = li;          // copy temporary buffer back to level data
3519
3520   return real_chunk_size;
3521 }
3522
3523 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3524 {
3525   int element = getMappedElement(getFile16BitBE(file));
3526   int envelope_nr = element - EL_ENVELOPE_1;
3527   int real_chunk_size = 2;
3528
3529   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3530
3531   while (!checkEndOfFile(file))
3532   {
3533     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3534                                             -1, element);
3535
3536     if (real_chunk_size >= chunk_size)
3537       break;
3538   }
3539
3540   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3541
3542   return real_chunk_size;
3543 }
3544
3545 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3546 {
3547   int element = getMappedElement(getFile16BitBE(file));
3548   int real_chunk_size = 2;
3549   struct ElementInfo *ei = &element_info[element];
3550   int i;
3551
3552   xx_ei = *ei;          // copy element data into temporary buffer
3553
3554   xx_ei.num_change_pages = -1;
3555
3556   while (!checkEndOfFile(file))
3557   {
3558     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3559                                             -1, element);
3560     if (xx_ei.num_change_pages != -1)
3561       break;
3562
3563     if (real_chunk_size >= chunk_size)
3564       break;
3565   }
3566
3567   *ei = xx_ei;
3568
3569   if (ei->num_change_pages == -1)
3570   {
3571     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3572          EL_NAME(element));
3573
3574     ei->num_change_pages = 1;
3575
3576     setElementChangePages(ei, 1);
3577     setElementChangeInfoToDefaults(ei->change);
3578
3579     return real_chunk_size;
3580   }
3581
3582   // initialize number of change pages stored for this custom element
3583   setElementChangePages(ei, ei->num_change_pages);
3584   for (i = 0; i < ei->num_change_pages; i++)
3585     setElementChangeInfoToDefaults(&ei->change_page[i]);
3586
3587   // start with reading properties for the first change page
3588   xx_current_change_page = 0;
3589
3590   while (!checkEndOfFile(file))
3591   {
3592     // level file might contain invalid change page number
3593     if (xx_current_change_page >= ei->num_change_pages)
3594       break;
3595
3596     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3597
3598     xx_change = *change;        // copy change data into temporary buffer
3599
3600     resetEventBits();           // reset bits; change page might have changed
3601
3602     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3603                                             -1, element);
3604
3605     *change = xx_change;
3606
3607     setEventFlagsFromEventBits(change);
3608
3609     if (real_chunk_size >= chunk_size)
3610       break;
3611   }
3612
3613   level->file_has_custom_elements = TRUE;
3614
3615   return real_chunk_size;
3616 }
3617
3618 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3619 {
3620   int element = getMappedElement(getFile16BitBE(file));
3621   int real_chunk_size = 2;
3622   struct ElementInfo *ei = &element_info[element];
3623   struct ElementGroupInfo *group = ei->group;
3624
3625   if (group == NULL)
3626     return -1;
3627
3628   xx_ei = *ei;          // copy element data into temporary buffer
3629   xx_group = *group;    // copy group data into temporary buffer
3630
3631   while (!checkEndOfFile(file))
3632   {
3633     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3634                                             -1, element);
3635
3636     if (real_chunk_size >= chunk_size)
3637       break;
3638   }
3639
3640   *ei = xx_ei;
3641   *group = xx_group;
3642
3643   level->file_has_custom_elements = TRUE;
3644
3645   return real_chunk_size;
3646 }
3647
3648 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3649 {
3650   int element = getMappedElement(getFile16BitBE(file));
3651   int real_chunk_size = 2;
3652   struct ElementInfo *ei = &element_info[element];
3653
3654   xx_ei = *ei;          // copy element data into temporary buffer
3655
3656   while (!checkEndOfFile(file))
3657   {
3658     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3659                                             -1, element);
3660
3661     if (real_chunk_size >= chunk_size)
3662       break;
3663   }
3664
3665   *ei = xx_ei;
3666
3667   level->file_has_custom_elements = TRUE;
3668
3669   return real_chunk_size;
3670 }
3671
3672 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3673                                       struct LevelFileInfo *level_file_info,
3674                                       boolean level_info_only)
3675 {
3676   char *filename = level_file_info->filename;
3677   char cookie[MAX_LINE_LEN];
3678   char chunk_name[CHUNK_ID_LEN + 1];
3679   int chunk_size;
3680   File *file;
3681
3682   if (!(file = openFile(filename, MODE_READ)))
3683   {
3684     level->no_valid_file = TRUE;
3685     level->no_level_file = TRUE;
3686
3687     if (level_info_only)
3688       return;
3689
3690     Warn("cannot read level '%s' -- using empty level", filename);
3691
3692     if (!setup.editor.use_template_for_new_levels)
3693       return;
3694
3695     // if level file not found, try to initialize level data from template
3696     filename = getGlobalLevelTemplateFilename();
3697
3698     if (!(file = openFile(filename, MODE_READ)))
3699       return;
3700
3701     // default: for empty levels, use level template for custom elements
3702     level->use_custom_template = TRUE;
3703
3704     level->no_valid_file = FALSE;
3705   }
3706
3707   getFileChunkBE(file, chunk_name, NULL);
3708   if (strEqual(chunk_name, "RND1"))
3709   {
3710     getFile32BitBE(file);               // not used
3711
3712     getFileChunkBE(file, chunk_name, NULL);
3713     if (!strEqual(chunk_name, "CAVE"))
3714     {
3715       level->no_valid_file = TRUE;
3716
3717       Warn("unknown format of level file '%s'", filename);
3718
3719       closeFile(file);
3720
3721       return;
3722     }
3723   }
3724   else  // check for pre-2.0 file format with cookie string
3725   {
3726     strcpy(cookie, chunk_name);
3727     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3728       cookie[4] = '\0';
3729     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3730       cookie[strlen(cookie) - 1] = '\0';
3731
3732     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3733     {
3734       level->no_valid_file = TRUE;
3735
3736       Warn("unknown format of level file '%s'", filename);
3737
3738       closeFile(file);
3739
3740       return;
3741     }
3742
3743     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3744     {
3745       level->no_valid_file = TRUE;
3746
3747       Warn("unsupported version of level file '%s'", filename);
3748
3749       closeFile(file);
3750
3751       return;
3752     }
3753
3754     // pre-2.0 level files have no game version, so use file version here
3755     level->game_version = level->file_version;
3756   }
3757
3758   if (level->file_version < FILE_VERSION_1_2)
3759   {
3760     // level files from versions before 1.2.0 without chunk structure
3761     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3762     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3763   }
3764   else
3765   {
3766     static struct
3767     {
3768       char *name;
3769       int size;
3770       int (*loader)(File *, int, struct LevelInfo *);
3771     }
3772     chunk_info[] =
3773     {
3774       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3775       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3776       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3777       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3778       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3779       { "INFO", -1,                     LoadLevel_INFO },
3780       { "BODY", -1,                     LoadLevel_BODY },
3781       { "CONT", -1,                     LoadLevel_CONT },
3782       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3783       { "CNT3", -1,                     LoadLevel_CNT3 },
3784       { "CUS1", -1,                     LoadLevel_CUS1 },
3785       { "CUS2", -1,                     LoadLevel_CUS2 },
3786       { "CUS3", -1,                     LoadLevel_CUS3 },
3787       { "CUS4", -1,                     LoadLevel_CUS4 },
3788       { "GRP1", -1,                     LoadLevel_GRP1 },
3789       { "CONF", -1,                     LoadLevel_CONF },
3790       { "ELEM", -1,                     LoadLevel_ELEM },
3791       { "NOTE", -1,                     LoadLevel_NOTE },
3792       { "CUSX", -1,                     LoadLevel_CUSX },
3793       { "GRPX", -1,                     LoadLevel_GRPX },
3794       { "EMPX", -1,                     LoadLevel_EMPX },
3795
3796       {  NULL,  0,                      NULL }
3797     };
3798
3799     while (getFileChunkBE(file, chunk_name, &chunk_size))
3800     {
3801       int i = 0;
3802
3803       while (chunk_info[i].name != NULL &&
3804              !strEqual(chunk_name, chunk_info[i].name))
3805         i++;
3806
3807       if (chunk_info[i].name == NULL)
3808       {
3809         Warn("unknown chunk '%s' in level file '%s'",
3810              chunk_name, filename);
3811
3812         ReadUnusedBytesFromFile(file, chunk_size);
3813       }
3814       else if (chunk_info[i].size != -1 &&
3815                chunk_info[i].size != chunk_size)
3816       {
3817         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3818              chunk_size, chunk_name, filename);
3819
3820         ReadUnusedBytesFromFile(file, chunk_size);
3821       }
3822       else
3823       {
3824         // call function to load this level chunk
3825         int chunk_size_expected =
3826           (chunk_info[i].loader)(file, chunk_size, level);
3827
3828         if (chunk_size_expected < 0)
3829         {
3830           Warn("error reading chunk '%s' in level file '%s'",
3831                chunk_name, filename);
3832
3833           break;
3834         }
3835
3836         // the size of some chunks cannot be checked before reading other
3837         // chunks first (like "HEAD" and "BODY") that contain some header
3838         // information, so check them here
3839         if (chunk_size_expected != chunk_size)
3840         {
3841           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3842                chunk_size, chunk_name, filename);
3843
3844           break;
3845         }
3846       }
3847     }
3848   }
3849
3850   closeFile(file);
3851 }
3852
3853
3854 // ----------------------------------------------------------------------------
3855 // functions for loading BD level
3856 // ----------------------------------------------------------------------------
3857
3858 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3859 {
3860   struct LevelInfo_BD *level_bd = level->native_bd_level;
3861   GdCave *cave = NULL;  // will be changed below
3862   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3863   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3864   int x, y;
3865
3866   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3867
3868   // cave and map newly allocated when set to defaults above
3869   cave = level_bd->cave;
3870
3871   // level type
3872   cave->intermission                    = level->bd_intermission;
3873
3874   // level settings
3875   cave->level_time[0]                   = level->time;
3876   cave->level_diamonds[0]               = level->gems_needed;
3877
3878   // game timing
3879   cave->scheduling                      = level->bd_scheduling_type;
3880   cave->pal_timing                      = level->bd_pal_timing;
3881   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
3882   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
3883   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
3884   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
3885
3886   // scores
3887   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
3888   cave->diamond_value                   = level->score[SC_EMERALD];
3889   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
3890
3891   // compatibility settings
3892   cave->lineshift                       = level->bd_line_shifting_borders;
3893   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
3894   cave->short_explosions                = level->bd_short_explosions;
3895   cave->gravity_affects_all             = level->bd_gravity_affects_all;
3896
3897   // player properties
3898   cave->diagonal_movements              = level->bd_diagonal_movements;
3899   cave->active_is_first_found           = level->bd_topmost_player_active;
3900   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
3901   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
3902   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3903
3904   // element properties
3905   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
3906   cave->level_magic_wall_time[0]        = level->time_magic_wall;
3907   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
3908   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
3909   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
3910   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
3911   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
3912   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
3913   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
3914   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
3915   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
3916   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
3917   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
3918   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
3919   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
3920
3921   cave->amoeba_too_big_effect       = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
3922   cave->amoeba_enclosed_effect      = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
3923   cave->amoeba_2_too_big_effect     = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
3924   cave->amoeba_2_enclosed_effect    = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
3925   cave->amoeba_2_explosion_effect   = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
3926   cave->amoeba_2_looks_like         = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
3927
3928   // level name
3929   strncpy(cave->name, level->name, sizeof(GdString));
3930   cave->name[sizeof(GdString) - 1] = '\0';
3931
3932   // playfield elements
3933   for (x = 0; x < cave->w; x++)
3934     for (y = 0; y < cave->h; y++)
3935       cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3936 }
3937
3938 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3939 {
3940   struct LevelInfo_BD *level_bd = level->native_bd_level;
3941   GdCave *cave = level_bd->cave;
3942   int bd_level_nr = level_bd->level_nr;
3943   int x, y;
3944
3945   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3946   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3947
3948   // level type
3949   level->bd_intermission                = cave->intermission;
3950
3951   // level settings
3952   level->time                           = cave->level_time[bd_level_nr];
3953   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
3954
3955   // game timing
3956   level->bd_scheduling_type             = cave->scheduling;
3957   level->bd_pal_timing                  = cave->pal_timing;
3958   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
3959   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
3960   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
3961   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
3962
3963   // scores
3964   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
3965   level->score[SC_EMERALD]              = cave->diamond_value;
3966   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
3967
3968   // compatibility settings
3969   level->bd_line_shifting_borders       = cave->lineshift;
3970   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
3971   level->bd_short_explosions            = cave->short_explosions;
3972   level->bd_gravity_affects_all         = cave->gravity_affects_all;
3973
3974   // player properties
3975   level->bd_diagonal_movements          = cave->diagonal_movements;
3976   level->bd_topmost_player_active       = cave->active_is_first_found;
3977   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
3978   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
3979   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
3980
3981   // element properties
3982   level->bd_clock_extra_time            = cave->level_bonus_time[0];
3983   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
3984   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
3985   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
3986   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
3987   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
3988   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
3989   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[0];
3990   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[0];
3991   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
3992   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
3993   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[0];
3994   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[0];
3995   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
3996   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
3997
3998   level->bd_amoeba_content_too_big      = map_element_BD_to_RND(cave->amoeba_too_big_effect);
3999   level->bd_amoeba_content_enclosed     = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4000   level->bd_amoeba_2_content_too_big    = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4001   level->bd_amoeba_2_content_enclosed   = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4002   level->bd_amoeba_2_content_exploding  = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4003   level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4004
4005   // level name
4006   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4007
4008   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4009   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4010
4011   // playfield elements
4012   for (x = 0; x < level->fieldx; x++)
4013     for (y = 0; y < level->fieldy; y++)
4014       level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4015
4016   checked_free(cave_name);
4017 }
4018
4019 static void setTapeInfoToDefaults(void);
4020
4021 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4022 {
4023   struct LevelInfo_BD *level_bd = level->native_bd_level;
4024   GdCave *cave = level_bd->cave;
4025   GdReplay *replay = level_bd->replay;
4026   int i;
4027
4028   if (replay == NULL)
4029     return;
4030
4031   // always start with reliable default values
4032   setTapeInfoToDefaults();
4033
4034   tape.level_nr = level_nr;             // (currently not used)
4035   tape.random_seed = replay->seed;
4036
4037   TapeSetDateFromIsoDateString(replay->date);
4038
4039   tape.counter = 0;
4040   tape.pos[tape.counter].delay = 0;
4041
4042   tape.bd_replay = TRUE;
4043
4044   // all time calculations only used to display approximate tape time
4045   int cave_speed = cave->speed;
4046   int milliseconds_game = 0;
4047   int milliseconds_elapsed = 20;
4048
4049   for (i = 0; i < replay->movements->len; i++)
4050   {
4051     int replay_action = replay->movements->data[i];
4052     int tape_action = map_action_BD_to_RND(replay_action);
4053     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4054     boolean success = 0;
4055
4056     while (1)
4057     {
4058       success = TapeAddAction(action);
4059
4060       milliseconds_game += milliseconds_elapsed;
4061
4062       if (milliseconds_game >= cave_speed)
4063       {
4064         milliseconds_game -= cave_speed;
4065
4066         break;
4067       }
4068     }
4069
4070     tape.counter++;
4071     tape.pos[tape.counter].delay = 0;
4072     tape.pos[tape.counter].action[0] = 0;
4073
4074     if (!success)
4075     {
4076       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4077
4078       break;
4079     }
4080   }
4081
4082   TapeHaltRecording();
4083 }
4084
4085
4086 // ----------------------------------------------------------------------------
4087 // functions for loading EM level
4088 // ----------------------------------------------------------------------------
4089
4090 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4091 {
4092   static int ball_xy[8][2] =
4093   {
4094     { 0, 0 },
4095     { 1, 0 },
4096     { 2, 0 },
4097     { 0, 1 },
4098     { 2, 1 },
4099     { 0, 2 },
4100     { 1, 2 },
4101     { 2, 2 },
4102   };
4103   struct LevelInfo_EM *level_em = level->native_em_level;
4104   struct CAVE *cav = level_em->cav;
4105   int i, j, x, y;
4106
4107   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4108   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4109
4110   cav->time_seconds     = level->time;
4111   cav->gems_needed      = level->gems_needed;
4112
4113   cav->emerald_score    = level->score[SC_EMERALD];
4114   cav->diamond_score    = level->score[SC_DIAMOND];
4115   cav->alien_score      = level->score[SC_ROBOT];
4116   cav->tank_score       = level->score[SC_SPACESHIP];
4117   cav->bug_score        = level->score[SC_BUG];
4118   cav->eater_score      = level->score[SC_YAMYAM];
4119   cav->nut_score        = level->score[SC_NUT];
4120   cav->dynamite_score   = level->score[SC_DYNAMITE];
4121   cav->key_score        = level->score[SC_KEY];
4122   cav->exit_score       = level->score[SC_TIME_BONUS];
4123
4124   cav->num_eater_arrays = level->num_yamyam_contents;
4125
4126   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4127     for (y = 0; y < 3; y++)
4128       for (x = 0; x < 3; x++)
4129         cav->eater_array[i][y * 3 + x] =
4130           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4131
4132   cav->amoeba_time              = level->amoeba_speed;
4133   cav->wonderwall_time          = level->time_magic_wall;
4134   cav->wheel_time               = level->time_wheel;
4135
4136   cav->android_move_time        = level->android_move_time;
4137   cav->android_clone_time       = level->android_clone_time;
4138   cav->ball_random              = level->ball_random;
4139   cav->ball_active              = level->ball_active_initial;
4140   cav->ball_time                = level->ball_time;
4141   cav->num_ball_arrays          = level->num_ball_contents;
4142
4143   cav->lenses_score             = level->lenses_score;
4144   cav->magnify_score            = level->magnify_score;
4145   cav->slurp_score              = level->slurp_score;
4146
4147   cav->lenses_time              = level->lenses_time;
4148   cav->magnify_time             = level->magnify_time;
4149
4150   cav->wind_time = 9999;
4151   cav->wind_direction =
4152     map_direction_RND_to_EM(level->wind_direction_initial);
4153
4154   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4155     for (j = 0; j < 8; j++)
4156       cav->ball_array[i][j] =
4157         map_element_RND_to_EM_cave(level->ball_content[i].
4158                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4159
4160   map_android_clone_elements_RND_to_EM(level);
4161
4162   // first fill the complete playfield with the empty space element
4163   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4164     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4165       cav->cave[x][y] = Cblank;
4166
4167   // then copy the real level contents from level file into the playfield
4168   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4169   {
4170     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4171
4172     if (level->field[x][y] == EL_AMOEBA_DEAD)
4173       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4174
4175     cav->cave[x][y] = new_element;
4176   }
4177
4178   for (i = 0; i < MAX_PLAYERS; i++)
4179   {
4180     cav->player_x[i] = -1;
4181     cav->player_y[i] = -1;
4182   }
4183
4184   // initialize player positions and delete players from the playfield
4185   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4186   {
4187     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4188     {
4189       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4190
4191       cav->player_x[player_nr] = x;
4192       cav->player_y[player_nr] = y;
4193
4194       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4195     }
4196   }
4197 }
4198
4199 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4200 {
4201   static int ball_xy[8][2] =
4202   {
4203     { 0, 0 },
4204     { 1, 0 },
4205     { 2, 0 },
4206     { 0, 1 },
4207     { 2, 1 },
4208     { 0, 2 },
4209     { 1, 2 },
4210     { 2, 2 },
4211   };
4212   struct LevelInfo_EM *level_em = level->native_em_level;
4213   struct CAVE *cav = level_em->cav;
4214   int i, j, x, y;
4215
4216   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4217   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4218
4219   level->time        = cav->time_seconds;
4220   level->gems_needed = cav->gems_needed;
4221
4222   sprintf(level->name, "Level %d", level->file_info.nr);
4223
4224   level->score[SC_EMERALD]      = cav->emerald_score;
4225   level->score[SC_DIAMOND]      = cav->diamond_score;
4226   level->score[SC_ROBOT]        = cav->alien_score;
4227   level->score[SC_SPACESHIP]    = cav->tank_score;
4228   level->score[SC_BUG]          = cav->bug_score;
4229   level->score[SC_YAMYAM]       = cav->eater_score;
4230   level->score[SC_NUT]          = cav->nut_score;
4231   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4232   level->score[SC_KEY]          = cav->key_score;
4233   level->score[SC_TIME_BONUS]   = cav->exit_score;
4234
4235   level->num_yamyam_contents    = cav->num_eater_arrays;
4236
4237   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4238     for (y = 0; y < 3; y++)
4239       for (x = 0; x < 3; x++)
4240         level->yamyam_content[i].e[x][y] =
4241           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4242
4243   level->amoeba_speed           = cav->amoeba_time;
4244   level->time_magic_wall        = cav->wonderwall_time;
4245   level->time_wheel             = cav->wheel_time;
4246
4247   level->android_move_time      = cav->android_move_time;
4248   level->android_clone_time     = cav->android_clone_time;
4249   level->ball_random            = cav->ball_random;
4250   level->ball_active_initial    = cav->ball_active;
4251   level->ball_time              = cav->ball_time;
4252   level->num_ball_contents      = cav->num_ball_arrays;
4253
4254   level->lenses_score           = cav->lenses_score;
4255   level->magnify_score          = cav->magnify_score;
4256   level->slurp_score            = cav->slurp_score;
4257
4258   level->lenses_time            = cav->lenses_time;
4259   level->magnify_time           = cav->magnify_time;
4260
4261   level->wind_direction_initial =
4262     map_direction_EM_to_RND(cav->wind_direction);
4263
4264   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4265     for (j = 0; j < 8; j++)
4266       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4267         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4268
4269   map_android_clone_elements_EM_to_RND(level);
4270
4271   // convert the playfield (some elements need special treatment)
4272   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4273   {
4274     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4275
4276     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4277       new_element = EL_AMOEBA_DEAD;
4278
4279     level->field[x][y] = new_element;
4280   }
4281
4282   for (i = 0; i < MAX_PLAYERS; i++)
4283   {
4284     // in case of all players set to the same field, use the first player
4285     int nr = MAX_PLAYERS - i - 1;
4286     int jx = cav->player_x[nr];
4287     int jy = cav->player_y[nr];
4288
4289     if (jx != -1 && jy != -1)
4290       level->field[jx][jy] = EL_PLAYER_1 + nr;
4291   }
4292
4293   // time score is counted for each 10 seconds left in Emerald Mine levels
4294   level->time_score_base = 10;
4295 }
4296
4297
4298 // ----------------------------------------------------------------------------
4299 // functions for loading SP level
4300 // ----------------------------------------------------------------------------
4301
4302 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4303 {
4304   struct LevelInfo_SP *level_sp = level->native_sp_level;
4305   LevelInfoType *header = &level_sp->header;
4306   int i, x, y;
4307
4308   level_sp->width  = level->fieldx;
4309   level_sp->height = level->fieldy;
4310
4311   for (x = 0; x < level->fieldx; x++)
4312     for (y = 0; y < level->fieldy; y++)
4313       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4314
4315   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4316
4317   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4318     header->LevelTitle[i] = level->name[i];
4319   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4320
4321   header->InfotronsNeeded = level->gems_needed;
4322
4323   header->SpecialPortCount = 0;
4324
4325   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4326   {
4327     boolean gravity_port_found = FALSE;
4328     boolean gravity_port_valid = FALSE;
4329     int gravity_port_flag;
4330     int gravity_port_base_element;
4331     int element = level->field[x][y];
4332
4333     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4334         element <= EL_SP_GRAVITY_ON_PORT_UP)
4335     {
4336       gravity_port_found = TRUE;
4337       gravity_port_valid = TRUE;
4338       gravity_port_flag = 1;
4339       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4340     }
4341     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4342              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4343     {
4344       gravity_port_found = TRUE;
4345       gravity_port_valid = TRUE;
4346       gravity_port_flag = 0;
4347       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4348     }
4349     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4350              element <= EL_SP_GRAVITY_PORT_UP)
4351     {
4352       // change R'n'D style gravity inverting special port to normal port
4353       // (there are no gravity inverting ports in native Supaplex engine)
4354
4355       gravity_port_found = TRUE;
4356       gravity_port_valid = FALSE;
4357       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4358     }
4359
4360     if (gravity_port_found)
4361     {
4362       if (gravity_port_valid &&
4363           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4364       {
4365         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4366
4367         port->PortLocation = (y * level->fieldx + x) * 2;
4368         port->Gravity = gravity_port_flag;
4369
4370         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4371
4372         header->SpecialPortCount++;
4373       }
4374       else
4375       {
4376         // change special gravity port to normal port
4377
4378         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4379       }
4380
4381       level_sp->playfield[x][y] = element - EL_SP_START;
4382     }
4383   }
4384 }
4385
4386 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4387 {
4388   struct LevelInfo_SP *level_sp = level->native_sp_level;
4389   LevelInfoType *header = &level_sp->header;
4390   boolean num_invalid_elements = 0;
4391   int i, j, x, y;
4392
4393   level->fieldx = level_sp->width;
4394   level->fieldy = level_sp->height;
4395
4396   for (x = 0; x < level->fieldx; x++)
4397   {
4398     for (y = 0; y < level->fieldy; y++)
4399     {
4400       int element_old = level_sp->playfield[x][y];
4401       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4402
4403       if (element_new == EL_UNKNOWN)
4404       {
4405         num_invalid_elements++;
4406
4407         Debug("level:native:SP", "invalid element %d at position %d, %d",
4408               element_old, x, y);
4409       }
4410
4411       level->field[x][y] = element_new;
4412     }
4413   }
4414
4415   if (num_invalid_elements > 0)
4416     Warn("found %d invalid elements%s", num_invalid_elements,
4417          (!options.debug ? " (use '--debug' for more details)" : ""));
4418
4419   for (i = 0; i < MAX_PLAYERS; i++)
4420     level->initial_player_gravity[i] =
4421       (header->InitialGravity == 1 ? TRUE : FALSE);
4422
4423   // skip leading spaces
4424   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4425     if (header->LevelTitle[i] != ' ')
4426       break;
4427
4428   // copy level title
4429   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4430     level->name[j] = header->LevelTitle[i];
4431   level->name[j] = '\0';
4432
4433   // cut trailing spaces
4434   for (; j > 0; j--)
4435     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4436       level->name[j - 1] = '\0';
4437
4438   level->gems_needed = header->InfotronsNeeded;
4439
4440   for (i = 0; i < header->SpecialPortCount; i++)
4441   {
4442     SpecialPortType *port = &header->SpecialPort[i];
4443     int port_location = port->PortLocation;
4444     int gravity = port->Gravity;
4445     int port_x, port_y, port_element;
4446
4447     port_x = (port_location / 2) % level->fieldx;
4448     port_y = (port_location / 2) / level->fieldx;
4449
4450     if (port_x < 0 || port_x >= level->fieldx ||
4451         port_y < 0 || port_y >= level->fieldy)
4452     {
4453       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4454
4455       continue;
4456     }
4457
4458     port_element = level->field[port_x][port_y];
4459
4460     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4461         port_element > EL_SP_GRAVITY_PORT_UP)
4462     {
4463       Warn("no special port at position (%d, %d)", port_x, port_y);
4464
4465       continue;
4466     }
4467
4468     // change previous (wrong) gravity inverting special port to either
4469     // gravity enabling special port or gravity disabling special port
4470     level->field[port_x][port_y] +=
4471       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4472        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4473   }
4474
4475   // change special gravity ports without database entries to normal ports
4476   for (x = 0; x < level->fieldx; x++)
4477     for (y = 0; y < level->fieldy; y++)
4478       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4479           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4480         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4481
4482   level->time = 0;                      // no time limit
4483   level->amoeba_speed = 0;
4484   level->time_magic_wall = 0;
4485   level->time_wheel = 0;
4486   level->amoeba_content = EL_EMPTY;
4487
4488   // original Supaplex does not use score values -- rate by playing time
4489   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4490     level->score[i] = 0;
4491
4492   level->rate_time_over_score = TRUE;
4493
4494   // there are no yamyams in supaplex levels
4495   for (i = 0; i < level->num_yamyam_contents; i++)
4496     for (x = 0; x < 3; x++)
4497       for (y = 0; y < 3; y++)
4498         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4499 }
4500
4501 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4502 {
4503   struct LevelInfo_SP *level_sp = level->native_sp_level;
4504   struct DemoInfo_SP *demo = &level_sp->demo;
4505   int i, j;
4506
4507   // always start with reliable default values
4508   demo->is_available = FALSE;
4509   demo->length = 0;
4510
4511   if (TAPE_IS_EMPTY(tape))
4512     return;
4513
4514   demo->level_nr = tape.level_nr;       // (currently not used)
4515
4516   level_sp->header.DemoRandomSeed = tape.random_seed;
4517
4518   demo->length = 0;
4519
4520   for (i = 0; i < tape.length; i++)
4521   {
4522     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4523     int demo_repeat = tape.pos[i].delay;
4524     int demo_entries = (demo_repeat + 15) / 16;
4525
4526     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4527     {
4528       Warn("tape truncated: size exceeds maximum SP demo size %d",
4529            SP_MAX_TAPE_LEN);
4530
4531       break;
4532     }
4533
4534     for (j = 0; j < demo_repeat / 16; j++)
4535       demo->data[demo->length++] = 0xf0 | demo_action;
4536
4537     if (demo_repeat % 16)
4538       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4539   }
4540
4541   demo->is_available = TRUE;
4542 }
4543
4544 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4545 {
4546   struct LevelInfo_SP *level_sp = level->native_sp_level;
4547   struct DemoInfo_SP *demo = &level_sp->demo;
4548   char *filename = level->file_info.filename;
4549   int i;
4550
4551   // always start with reliable default values
4552   setTapeInfoToDefaults();
4553
4554   if (!demo->is_available)
4555     return;
4556
4557   tape.level_nr = demo->level_nr;       // (currently not used)
4558   tape.random_seed = level_sp->header.DemoRandomSeed;
4559
4560   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4561
4562   tape.counter = 0;
4563   tape.pos[tape.counter].delay = 0;
4564
4565   for (i = 0; i < demo->length; i++)
4566   {
4567     int demo_action = demo->data[i] & 0x0f;
4568     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4569     int tape_action = map_key_SP_to_RND(demo_action);
4570     int tape_repeat = demo_repeat + 1;
4571     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4572     boolean success = 0;
4573     int j;
4574
4575     for (j = 0; j < tape_repeat; j++)
4576       success = TapeAddAction(action);
4577
4578     if (!success)
4579     {
4580       Warn("SP demo truncated: size exceeds maximum tape size %d",
4581            MAX_TAPE_LEN);
4582
4583       break;
4584     }
4585   }
4586
4587   TapeHaltRecording();
4588 }
4589
4590
4591 // ----------------------------------------------------------------------------
4592 // functions for loading MM level
4593 // ----------------------------------------------------------------------------
4594
4595 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4596 {
4597   struct LevelInfo_MM *level_mm = level->native_mm_level;
4598   int i, x, y;
4599
4600   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4601   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4602
4603   level_mm->time = level->time;
4604   level_mm->kettles_needed = level->gems_needed;
4605   level_mm->auto_count_kettles = level->auto_count_gems;
4606
4607   level_mm->mm_laser_red   = level->mm_laser_red;
4608   level_mm->mm_laser_green = level->mm_laser_green;
4609   level_mm->mm_laser_blue  = level->mm_laser_blue;
4610
4611   level_mm->df_laser_red   = level->df_laser_red;
4612   level_mm->df_laser_green = level->df_laser_green;
4613   level_mm->df_laser_blue  = level->df_laser_blue;
4614
4615   strcpy(level_mm->name, level->name);
4616   strcpy(level_mm->author, level->author);
4617
4618   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4619   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4620   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4621   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4622   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4623
4624   level_mm->amoeba_speed = level->amoeba_speed;
4625   level_mm->time_fuse    = level->mm_time_fuse;
4626   level_mm->time_bomb    = level->mm_time_bomb;
4627   level_mm->time_ball    = level->mm_time_ball;
4628   level_mm->time_block   = level->mm_time_block;
4629
4630   level_mm->num_ball_contents = level->num_mm_ball_contents;
4631   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4632   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4633   level_mm->explode_ball = level->explode_mm_ball;
4634
4635   for (i = 0; i < level->num_mm_ball_contents; i++)
4636     level_mm->ball_content[i] =
4637       map_element_RND_to_MM(level->mm_ball_content[i]);
4638
4639   for (x = 0; x < level->fieldx; x++)
4640     for (y = 0; y < level->fieldy; y++)
4641       Ur[x][y] =
4642         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4643 }
4644
4645 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4646 {
4647   struct LevelInfo_MM *level_mm = level->native_mm_level;
4648   int i, x, y;
4649
4650   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4651   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4652
4653   level->time = level_mm->time;
4654   level->gems_needed = level_mm->kettles_needed;
4655   level->auto_count_gems = level_mm->auto_count_kettles;
4656
4657   level->mm_laser_red   = level_mm->mm_laser_red;
4658   level->mm_laser_green = level_mm->mm_laser_green;
4659   level->mm_laser_blue  = level_mm->mm_laser_blue;
4660
4661   level->df_laser_red   = level_mm->df_laser_red;
4662   level->df_laser_green = level_mm->df_laser_green;
4663   level->df_laser_blue  = level_mm->df_laser_blue;
4664
4665   strcpy(level->name, level_mm->name);
4666
4667   // only overwrite author from 'levelinfo.conf' if author defined in level
4668   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4669     strcpy(level->author, level_mm->author);
4670
4671   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4672   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4673   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4674   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4675   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4676
4677   level->amoeba_speed  = level_mm->amoeba_speed;
4678   level->mm_time_fuse  = level_mm->time_fuse;
4679   level->mm_time_bomb  = level_mm->time_bomb;
4680   level->mm_time_ball  = level_mm->time_ball;
4681   level->mm_time_block = level_mm->time_block;
4682
4683   level->num_mm_ball_contents = level_mm->num_ball_contents;
4684   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4685   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4686   level->explode_mm_ball = level_mm->explode_ball;
4687
4688   for (i = 0; i < level->num_mm_ball_contents; i++)
4689     level->mm_ball_content[i] =
4690       map_element_MM_to_RND(level_mm->ball_content[i]);
4691
4692   for (x = 0; x < level->fieldx; x++)
4693     for (y = 0; y < level->fieldy; y++)
4694       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4695 }
4696
4697
4698 // ----------------------------------------------------------------------------
4699 // functions for loading DC level
4700 // ----------------------------------------------------------------------------
4701
4702 #define DC_LEVEL_HEADER_SIZE            344
4703
4704 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4705                                         boolean init)
4706 {
4707   static int last_data_encoded;
4708   static int offset1;
4709   static int offset2;
4710   int diff;
4711   int diff_hi, diff_lo;
4712   int data_hi, data_lo;
4713   unsigned short data_decoded;
4714
4715   if (init)
4716   {
4717     last_data_encoded = 0;
4718     offset1 = -1;
4719     offset2 = 0;
4720
4721     return 0;
4722   }
4723
4724   diff = data_encoded - last_data_encoded;
4725   diff_hi = diff & ~0xff;
4726   diff_lo = diff &  0xff;
4727
4728   offset2 += diff_lo;
4729
4730   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4731   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4732   data_hi = data_hi & 0xff00;
4733
4734   data_decoded = data_hi | data_lo;
4735
4736   last_data_encoded = data_encoded;
4737
4738   offset1 = (offset1 + 1) % 31;
4739   offset2 = offset2 & 0xff;
4740
4741   return data_decoded;
4742 }
4743
4744 static int getMappedElement_DC(int element)
4745 {
4746   switch (element)
4747   {
4748     case 0x0000:
4749       element = EL_ROCK;
4750       break;
4751
4752       // 0x0117 - 0x036e: (?)
4753       // EL_DIAMOND
4754
4755       // 0x042d - 0x0684: (?)
4756       // EL_EMERALD
4757
4758     case 0x06f1:
4759       element = EL_NUT;
4760       break;
4761
4762     case 0x074c:
4763       element = EL_BOMB;
4764       break;
4765
4766     case 0x07a4:
4767       element = EL_PEARL;
4768       break;
4769
4770     case 0x0823:
4771       element = EL_CRYSTAL;
4772       break;
4773
4774     case 0x0e77:        // quicksand (boulder)
4775       element = EL_QUICKSAND_FAST_FULL;
4776       break;
4777
4778     case 0x0e99:        // slow quicksand (boulder)
4779       element = EL_QUICKSAND_FULL;
4780       break;
4781
4782     case 0x0ed2:
4783       element = EL_EM_EXIT_OPEN;
4784       break;
4785
4786     case 0x0ee3:
4787       element = EL_EM_EXIT_CLOSED;
4788       break;
4789
4790     case 0x0eeb:
4791       element = EL_EM_STEEL_EXIT_OPEN;
4792       break;
4793
4794     case 0x0efc:
4795       element = EL_EM_STEEL_EXIT_CLOSED;
4796       break;
4797
4798     case 0x0f4f:        // dynamite (lit 1)
4799       element = EL_EM_DYNAMITE_ACTIVE;
4800       break;
4801
4802     case 0x0f57:        // dynamite (lit 2)
4803       element = EL_EM_DYNAMITE_ACTIVE;
4804       break;
4805
4806     case 0x0f5f:        // dynamite (lit 3)
4807       element = EL_EM_DYNAMITE_ACTIVE;
4808       break;
4809
4810     case 0x0f67:        // dynamite (lit 4)
4811       element = EL_EM_DYNAMITE_ACTIVE;
4812       break;
4813
4814     case 0x0f81:
4815     case 0x0f82:
4816     case 0x0f83:
4817     case 0x0f84:
4818       element = EL_AMOEBA_WET;
4819       break;
4820
4821     case 0x0f85:
4822       element = EL_AMOEBA_DROP;
4823       break;
4824
4825     case 0x0fb9:
4826       element = EL_DC_MAGIC_WALL;
4827       break;
4828
4829     case 0x0fd0:
4830       element = EL_SPACESHIP_UP;
4831       break;
4832
4833     case 0x0fd9:
4834       element = EL_SPACESHIP_DOWN;
4835       break;
4836
4837     case 0x0ff1:
4838       element = EL_SPACESHIP_LEFT;
4839       break;
4840
4841     case 0x0ff9:
4842       element = EL_SPACESHIP_RIGHT;
4843       break;
4844
4845     case 0x1057:
4846       element = EL_BUG_UP;
4847       break;
4848
4849     case 0x1060:
4850       element = EL_BUG_DOWN;
4851       break;
4852
4853     case 0x1078:
4854       element = EL_BUG_LEFT;
4855       break;
4856
4857     case 0x1080:
4858       element = EL_BUG_RIGHT;
4859       break;
4860
4861     case 0x10de:
4862       element = EL_MOLE_UP;
4863       break;
4864
4865     case 0x10e7:
4866       element = EL_MOLE_DOWN;
4867       break;
4868
4869     case 0x10ff:
4870       element = EL_MOLE_LEFT;
4871       break;
4872
4873     case 0x1107:
4874       element = EL_MOLE_RIGHT;
4875       break;
4876
4877     case 0x11c0:
4878       element = EL_ROBOT;
4879       break;
4880
4881     case 0x13f5:
4882       element = EL_YAMYAM_UP;
4883       break;
4884
4885     case 0x1425:
4886       element = EL_SWITCHGATE_OPEN;
4887       break;
4888
4889     case 0x1426:
4890       element = EL_SWITCHGATE_CLOSED;
4891       break;
4892
4893     case 0x1437:
4894       element = EL_DC_SWITCHGATE_SWITCH_UP;
4895       break;
4896
4897     case 0x143a:
4898       element = EL_TIMEGATE_CLOSED;
4899       break;
4900
4901     case 0x144c:        // conveyor belt switch (green)
4902       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4903       break;
4904
4905     case 0x144f:        // conveyor belt switch (red)
4906       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4907       break;
4908
4909     case 0x1452:        // conveyor belt switch (blue)
4910       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4911       break;
4912
4913     case 0x145b:
4914       element = EL_CONVEYOR_BELT_3_MIDDLE;
4915       break;
4916
4917     case 0x1463:
4918       element = EL_CONVEYOR_BELT_3_LEFT;
4919       break;
4920
4921     case 0x146b:
4922       element = EL_CONVEYOR_BELT_3_RIGHT;
4923       break;
4924
4925     case 0x1473:
4926       element = EL_CONVEYOR_BELT_1_MIDDLE;
4927       break;
4928
4929     case 0x147b:
4930       element = EL_CONVEYOR_BELT_1_LEFT;
4931       break;
4932
4933     case 0x1483:
4934       element = EL_CONVEYOR_BELT_1_RIGHT;
4935       break;
4936
4937     case 0x148b:
4938       element = EL_CONVEYOR_BELT_4_MIDDLE;
4939       break;
4940
4941     case 0x1493:
4942       element = EL_CONVEYOR_BELT_4_LEFT;
4943       break;
4944
4945     case 0x149b:
4946       element = EL_CONVEYOR_BELT_4_RIGHT;
4947       break;
4948
4949     case 0x14ac:
4950       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4951       break;
4952
4953     case 0x14bd:
4954       element = EL_EXPANDABLE_WALL_VERTICAL;
4955       break;
4956
4957     case 0x14c6:
4958       element = EL_EXPANDABLE_WALL_ANY;
4959       break;
4960
4961     case 0x14ce:        // growing steel wall (left/right)
4962       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4963       break;
4964
4965     case 0x14df:        // growing steel wall (up/down)
4966       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4967       break;
4968
4969     case 0x14e8:        // growing steel wall (up/down/left/right)
4970       element = EL_EXPANDABLE_STEELWALL_ANY;
4971       break;
4972
4973     case 0x14e9:
4974       element = EL_SHIELD_DEADLY;
4975       break;
4976
4977     case 0x1501:
4978       element = EL_EXTRA_TIME;
4979       break;
4980
4981     case 0x154f:
4982       element = EL_ACID;
4983       break;
4984
4985     case 0x1577:
4986       element = EL_EMPTY_SPACE;
4987       break;
4988
4989     case 0x1578:        // quicksand (empty)
4990       element = EL_QUICKSAND_FAST_EMPTY;
4991       break;
4992
4993     case 0x1579:        // slow quicksand (empty)
4994       element = EL_QUICKSAND_EMPTY;
4995       break;
4996
4997       // 0x157c - 0x158b:
4998       // EL_SAND
4999
5000       // 0x1590 - 0x159f:
5001       // EL_DC_LANDMINE
5002
5003     case 0x15a0:
5004       element = EL_EM_DYNAMITE;
5005       break;
5006
5007     case 0x15a1:        // key (red)
5008       element = EL_EM_KEY_1;
5009       break;
5010
5011     case 0x15a2:        // key (yellow)
5012       element = EL_EM_KEY_2;
5013       break;
5014
5015     case 0x15a3:        // key (blue)
5016       element = EL_EM_KEY_4;
5017       break;
5018
5019     case 0x15a4:        // key (green)
5020       element = EL_EM_KEY_3;
5021       break;
5022
5023     case 0x15a5:        // key (white)
5024       element = EL_DC_KEY_WHITE;
5025       break;
5026
5027     case 0x15a6:
5028       element = EL_WALL_SLIPPERY;
5029       break;
5030
5031     case 0x15a7:
5032       element = EL_WALL;
5033       break;
5034
5035     case 0x15a8:        // wall (not round)
5036       element = EL_WALL;
5037       break;
5038
5039     case 0x15a9:        // (blue)
5040       element = EL_CHAR_A;
5041       break;
5042
5043     case 0x15aa:        // (blue)
5044       element = EL_CHAR_B;
5045       break;
5046
5047     case 0x15ab:        // (blue)
5048       element = EL_CHAR_C;
5049       break;
5050
5051     case 0x15ac:        // (blue)
5052       element = EL_CHAR_D;
5053       break;
5054
5055     case 0x15ad:        // (blue)
5056       element = EL_CHAR_E;
5057       break;
5058
5059     case 0x15ae:        // (blue)
5060       element = EL_CHAR_F;
5061       break;
5062
5063     case 0x15af:        // (blue)
5064       element = EL_CHAR_G;
5065       break;
5066
5067     case 0x15b0:        // (blue)
5068       element = EL_CHAR_H;
5069       break;
5070
5071     case 0x15b1:        // (blue)
5072       element = EL_CHAR_I;
5073       break;
5074
5075     case 0x15b2:        // (blue)
5076       element = EL_CHAR_J;
5077       break;
5078
5079     case 0x15b3:        // (blue)
5080       element = EL_CHAR_K;
5081       break;
5082
5083     case 0x15b4:        // (blue)
5084       element = EL_CHAR_L;
5085       break;
5086
5087     case 0x15b5:        // (blue)
5088       element = EL_CHAR_M;
5089       break;
5090
5091     case 0x15b6:        // (blue)
5092       element = EL_CHAR_N;
5093       break;
5094
5095     case 0x15b7:        // (blue)
5096       element = EL_CHAR_O;
5097       break;
5098
5099     case 0x15b8:        // (blue)
5100       element = EL_CHAR_P;
5101       break;
5102
5103     case 0x15b9:        // (blue)
5104       element = EL_CHAR_Q;
5105       break;
5106
5107     case 0x15ba:        // (blue)
5108       element = EL_CHAR_R;
5109       break;
5110
5111     case 0x15bb:        // (blue)
5112       element = EL_CHAR_S;
5113       break;
5114
5115     case 0x15bc:        // (blue)
5116       element = EL_CHAR_T;
5117       break;
5118
5119     case 0x15bd:        // (blue)
5120       element = EL_CHAR_U;
5121       break;
5122
5123     case 0x15be:        // (blue)
5124       element = EL_CHAR_V;
5125       break;
5126
5127     case 0x15bf:        // (blue)
5128       element = EL_CHAR_W;
5129       break;
5130
5131     case 0x15c0:        // (blue)
5132       element = EL_CHAR_X;
5133       break;
5134
5135     case 0x15c1:        // (blue)
5136       element = EL_CHAR_Y;
5137       break;
5138
5139     case 0x15c2:        // (blue)
5140       element = EL_CHAR_Z;
5141       break;
5142
5143     case 0x15c3:        // (blue)
5144       element = EL_CHAR_AUMLAUT;
5145       break;
5146
5147     case 0x15c4:        // (blue)
5148       element = EL_CHAR_OUMLAUT;
5149       break;
5150
5151     case 0x15c5:        // (blue)
5152       element = EL_CHAR_UUMLAUT;
5153       break;
5154
5155     case 0x15c6:        // (blue)
5156       element = EL_CHAR_0;
5157       break;
5158
5159     case 0x15c7:        // (blue)
5160       element = EL_CHAR_1;
5161       break;
5162
5163     case 0x15c8:        // (blue)
5164       element = EL_CHAR_2;
5165       break;
5166
5167     case 0x15c9:        // (blue)
5168       element = EL_CHAR_3;
5169       break;
5170
5171     case 0x15ca:        // (blue)
5172       element = EL_CHAR_4;
5173       break;
5174
5175     case 0x15cb:        // (blue)
5176       element = EL_CHAR_5;
5177       break;
5178
5179     case 0x15cc:        // (blue)
5180       element = EL_CHAR_6;
5181       break;
5182
5183     case 0x15cd:        // (blue)
5184       element = EL_CHAR_7;
5185       break;
5186
5187     case 0x15ce:        // (blue)
5188       element = EL_CHAR_8;
5189       break;
5190
5191     case 0x15cf:        // (blue)
5192       element = EL_CHAR_9;
5193       break;
5194
5195     case 0x15d0:        // (blue)
5196       element = EL_CHAR_PERIOD;
5197       break;
5198
5199     case 0x15d1:        // (blue)
5200       element = EL_CHAR_EXCLAM;
5201       break;
5202
5203     case 0x15d2:        // (blue)
5204       element = EL_CHAR_COLON;
5205       break;
5206
5207     case 0x15d3:        // (blue)
5208       element = EL_CHAR_LESS;
5209       break;
5210
5211     case 0x15d4:        // (blue)
5212       element = EL_CHAR_GREATER;
5213       break;
5214
5215     case 0x15d5:        // (blue)
5216       element = EL_CHAR_QUESTION;
5217       break;
5218
5219     case 0x15d6:        // (blue)
5220       element = EL_CHAR_COPYRIGHT;
5221       break;
5222
5223     case 0x15d7:        // (blue)
5224       element = EL_CHAR_UP;
5225       break;
5226
5227     case 0x15d8:        // (blue)
5228       element = EL_CHAR_DOWN;
5229       break;
5230
5231     case 0x15d9:        // (blue)
5232       element = EL_CHAR_BUTTON;
5233       break;
5234
5235     case 0x15da:        // (blue)
5236       element = EL_CHAR_PLUS;
5237       break;
5238
5239     case 0x15db:        // (blue)
5240       element = EL_CHAR_MINUS;
5241       break;
5242
5243     case 0x15dc:        // (blue)
5244       element = EL_CHAR_APOSTROPHE;
5245       break;
5246
5247     case 0x15dd:        // (blue)
5248       element = EL_CHAR_PARENLEFT;
5249       break;
5250
5251     case 0x15de:        // (blue)
5252       element = EL_CHAR_PARENRIGHT;
5253       break;
5254
5255     case 0x15df:        // (green)
5256       element = EL_CHAR_A;
5257       break;
5258
5259     case 0x15e0:        // (green)
5260       element = EL_CHAR_B;
5261       break;
5262
5263     case 0x15e1:        // (green)
5264       element = EL_CHAR_C;
5265       break;
5266
5267     case 0x15e2:        // (green)
5268       element = EL_CHAR_D;
5269       break;
5270
5271     case 0x15e3:        // (green)
5272       element = EL_CHAR_E;
5273       break;
5274
5275     case 0x15e4:        // (green)
5276       element = EL_CHAR_F;
5277       break;
5278
5279     case 0x15e5:        // (green)
5280       element = EL_CHAR_G;
5281       break;
5282
5283     case 0x15e6:        // (green)
5284       element = EL_CHAR_H;
5285       break;
5286
5287     case 0x15e7:        // (green)
5288       element = EL_CHAR_I;
5289       break;
5290
5291     case 0x15e8:        // (green)
5292       element = EL_CHAR_J;
5293       break;
5294
5295     case 0x15e9:        // (green)
5296       element = EL_CHAR_K;
5297       break;
5298
5299     case 0x15ea:        // (green)
5300       element = EL_CHAR_L;
5301       break;
5302
5303     case 0x15eb:        // (green)
5304       element = EL_CHAR_M;
5305       break;
5306
5307     case 0x15ec:        // (green)
5308       element = EL_CHAR_N;
5309       break;
5310
5311     case 0x15ed:        // (green)
5312       element = EL_CHAR_O;
5313       break;
5314
5315     case 0x15ee:        // (green)
5316       element = EL_CHAR_P;
5317       break;
5318
5319     case 0x15ef:        // (green)
5320       element = EL_CHAR_Q;
5321       break;
5322
5323     case 0x15f0:        // (green)
5324       element = EL_CHAR_R;
5325       break;
5326
5327     case 0x15f1:        // (green)
5328       element = EL_CHAR_S;
5329       break;
5330
5331     case 0x15f2:        // (green)
5332       element = EL_CHAR_T;
5333       break;
5334
5335     case 0x15f3:        // (green)
5336       element = EL_CHAR_U;
5337       break;
5338
5339     case 0x15f4:        // (green)
5340       element = EL_CHAR_V;
5341       break;
5342
5343     case 0x15f5:        // (green)
5344       element = EL_CHAR_W;
5345       break;
5346
5347     case 0x15f6:        // (green)
5348       element = EL_CHAR_X;
5349       break;
5350
5351     case 0x15f7:        // (green)
5352       element = EL_CHAR_Y;
5353       break;
5354
5355     case 0x15f8:        // (green)
5356       element = EL_CHAR_Z;
5357       break;
5358
5359     case 0x15f9:        // (green)
5360       element = EL_CHAR_AUMLAUT;
5361       break;
5362
5363     case 0x15fa:        // (green)
5364       element = EL_CHAR_OUMLAUT;
5365       break;
5366
5367     case 0x15fb:        // (green)
5368       element = EL_CHAR_UUMLAUT;
5369       break;
5370
5371     case 0x15fc:        // (green)
5372       element = EL_CHAR_0;
5373       break;
5374
5375     case 0x15fd:        // (green)
5376       element = EL_CHAR_1;
5377       break;
5378
5379     case 0x15fe:        // (green)
5380       element = EL_CHAR_2;
5381       break;
5382
5383     case 0x15ff:        // (green)
5384       element = EL_CHAR_3;
5385       break;
5386
5387     case 0x1600:        // (green)
5388       element = EL_CHAR_4;
5389       break;
5390
5391     case 0x1601:        // (green)
5392       element = EL_CHAR_5;
5393       break;
5394
5395     case 0x1602:        // (green)
5396       element = EL_CHAR_6;
5397       break;
5398
5399     case 0x1603:        // (green)
5400       element = EL_CHAR_7;
5401       break;
5402
5403     case 0x1604:        // (green)
5404       element = EL_CHAR_8;
5405       break;
5406
5407     case 0x1605:        // (green)
5408       element = EL_CHAR_9;
5409       break;
5410
5411     case 0x1606:        // (green)
5412       element = EL_CHAR_PERIOD;
5413       break;
5414
5415     case 0x1607:        // (green)
5416       element = EL_CHAR_EXCLAM;
5417       break;
5418
5419     case 0x1608:        // (green)
5420       element = EL_CHAR_COLON;
5421       break;
5422
5423     case 0x1609:        // (green)
5424       element = EL_CHAR_LESS;
5425       break;
5426
5427     case 0x160a:        // (green)
5428       element = EL_CHAR_GREATER;
5429       break;
5430
5431     case 0x160b:        // (green)
5432       element = EL_CHAR_QUESTION;
5433       break;
5434
5435     case 0x160c:        // (green)
5436       element = EL_CHAR_COPYRIGHT;
5437       break;
5438
5439     case 0x160d:        // (green)
5440       element = EL_CHAR_UP;
5441       break;
5442
5443     case 0x160e:        // (green)
5444       element = EL_CHAR_DOWN;
5445       break;
5446
5447     case 0x160f:        // (green)
5448       element = EL_CHAR_BUTTON;
5449       break;
5450
5451     case 0x1610:        // (green)
5452       element = EL_CHAR_PLUS;
5453       break;
5454
5455     case 0x1611:        // (green)
5456       element = EL_CHAR_MINUS;
5457       break;
5458
5459     case 0x1612:        // (green)
5460       element = EL_CHAR_APOSTROPHE;
5461       break;
5462
5463     case 0x1613:        // (green)
5464       element = EL_CHAR_PARENLEFT;
5465       break;
5466
5467     case 0x1614:        // (green)
5468       element = EL_CHAR_PARENRIGHT;
5469       break;
5470
5471     case 0x1615:        // (blue steel)
5472       element = EL_STEEL_CHAR_A;
5473       break;
5474
5475     case 0x1616:        // (blue steel)
5476       element = EL_STEEL_CHAR_B;
5477       break;
5478
5479     case 0x1617:        // (blue steel)
5480       element = EL_STEEL_CHAR_C;
5481       break;
5482
5483     case 0x1618:        // (blue steel)
5484       element = EL_STEEL_CHAR_D;
5485       break;
5486
5487     case 0x1619:        // (blue steel)
5488       element = EL_STEEL_CHAR_E;
5489       break;
5490
5491     case 0x161a:        // (blue steel)
5492       element = EL_STEEL_CHAR_F;
5493       break;
5494
5495     case 0x161b:        // (blue steel)
5496       element = EL_STEEL_CHAR_G;
5497       break;
5498
5499     case 0x161c:        // (blue steel)
5500       element = EL_STEEL_CHAR_H;
5501       break;
5502
5503     case 0x161d:        // (blue steel)
5504       element = EL_STEEL_CHAR_I;
5505       break;
5506
5507     case 0x161e:        // (blue steel)
5508       element = EL_STEEL_CHAR_J;
5509       break;
5510
5511     case 0x161f:        // (blue steel)
5512       element = EL_STEEL_CHAR_K;
5513       break;
5514
5515     case 0x1620:        // (blue steel)
5516       element = EL_STEEL_CHAR_L;
5517       break;
5518
5519     case 0x1621:        // (blue steel)
5520       element = EL_STEEL_CHAR_M;
5521       break;
5522
5523     case 0x1622:        // (blue steel)
5524       element = EL_STEEL_CHAR_N;
5525       break;
5526
5527     case 0x1623:        // (blue steel)
5528       element = EL_STEEL_CHAR_O;
5529       break;
5530
5531     case 0x1624:        // (blue steel)
5532       element = EL_STEEL_CHAR_P;
5533       break;
5534
5535     case 0x1625:        // (blue steel)
5536       element = EL_STEEL_CHAR_Q;
5537       break;
5538
5539     case 0x1626:        // (blue steel)
5540       element = EL_STEEL_CHAR_R;
5541       break;
5542
5543     case 0x1627:        // (blue steel)
5544       element = EL_STEEL_CHAR_S;
5545       break;
5546
5547     case 0x1628:        // (blue steel)
5548       element = EL_STEEL_CHAR_T;
5549       break;
5550
5551     case 0x1629:        // (blue steel)
5552       element = EL_STEEL_CHAR_U;
5553       break;
5554
5555     case 0x162a:        // (blue steel)
5556       element = EL_STEEL_CHAR_V;
5557       break;
5558
5559     case 0x162b:        // (blue steel)
5560       element = EL_STEEL_CHAR_W;
5561       break;
5562
5563     case 0x162c:        // (blue steel)
5564       element = EL_STEEL_CHAR_X;
5565       break;
5566
5567     case 0x162d:        // (blue steel)
5568       element = EL_STEEL_CHAR_Y;
5569       break;
5570
5571     case 0x162e:        // (blue steel)
5572       element = EL_STEEL_CHAR_Z;
5573       break;
5574
5575     case 0x162f:        // (blue steel)
5576       element = EL_STEEL_CHAR_AUMLAUT;
5577       break;
5578
5579     case 0x1630:        // (blue steel)
5580       element = EL_STEEL_CHAR_OUMLAUT;
5581       break;
5582
5583     case 0x1631:        // (blue steel)
5584       element = EL_STEEL_CHAR_UUMLAUT;
5585       break;
5586
5587     case 0x1632:        // (blue steel)
5588       element = EL_STEEL_CHAR_0;
5589       break;
5590
5591     case 0x1633:        // (blue steel)
5592       element = EL_STEEL_CHAR_1;
5593       break;
5594
5595     case 0x1634:        // (blue steel)
5596       element = EL_STEEL_CHAR_2;
5597       break;
5598
5599     case 0x1635:        // (blue steel)
5600       element = EL_STEEL_CHAR_3;
5601       break;
5602
5603     case 0x1636:        // (blue steel)
5604       element = EL_STEEL_CHAR_4;
5605       break;
5606
5607     case 0x1637:        // (blue steel)
5608       element = EL_STEEL_CHAR_5;
5609       break;
5610
5611     case 0x1638:        // (blue steel)
5612       element = EL_STEEL_CHAR_6;
5613       break;
5614
5615     case 0x1639:        // (blue steel)
5616       element = EL_STEEL_CHAR_7;
5617       break;
5618
5619     case 0x163a:        // (blue steel)
5620       element = EL_STEEL_CHAR_8;
5621       break;
5622
5623     case 0x163b:        // (blue steel)
5624       element = EL_STEEL_CHAR_9;
5625       break;
5626
5627     case 0x163c:        // (blue steel)
5628       element = EL_STEEL_CHAR_PERIOD;
5629       break;
5630
5631     case 0x163d:        // (blue steel)
5632       element = EL_STEEL_CHAR_EXCLAM;
5633       break;
5634
5635     case 0x163e:        // (blue steel)
5636       element = EL_STEEL_CHAR_COLON;
5637       break;
5638
5639     case 0x163f:        // (blue steel)
5640       element = EL_STEEL_CHAR_LESS;
5641       break;
5642
5643     case 0x1640:        // (blue steel)
5644       element = EL_STEEL_CHAR_GREATER;
5645       break;
5646
5647     case 0x1641:        // (blue steel)
5648       element = EL_STEEL_CHAR_QUESTION;
5649       break;
5650
5651     case 0x1642:        // (blue steel)
5652       element = EL_STEEL_CHAR_COPYRIGHT;
5653       break;
5654
5655     case 0x1643:        // (blue steel)
5656       element = EL_STEEL_CHAR_UP;
5657       break;
5658
5659     case 0x1644:        // (blue steel)
5660       element = EL_STEEL_CHAR_DOWN;
5661       break;
5662
5663     case 0x1645:        // (blue steel)
5664       element = EL_STEEL_CHAR_BUTTON;
5665       break;
5666
5667     case 0x1646:        // (blue steel)
5668       element = EL_STEEL_CHAR_PLUS;
5669       break;
5670
5671     case 0x1647:        // (blue steel)
5672       element = EL_STEEL_CHAR_MINUS;
5673       break;
5674
5675     case 0x1648:        // (blue steel)
5676       element = EL_STEEL_CHAR_APOSTROPHE;
5677       break;
5678
5679     case 0x1649:        // (blue steel)
5680       element = EL_STEEL_CHAR_PARENLEFT;
5681       break;
5682
5683     case 0x164a:        // (blue steel)
5684       element = EL_STEEL_CHAR_PARENRIGHT;
5685       break;
5686
5687     case 0x164b:        // (green steel)
5688       element = EL_STEEL_CHAR_A;
5689       break;
5690
5691     case 0x164c:        // (green steel)
5692       element = EL_STEEL_CHAR_B;
5693       break;
5694
5695     case 0x164d:        // (green steel)
5696       element = EL_STEEL_CHAR_C;
5697       break;
5698
5699     case 0x164e:        // (green steel)
5700       element = EL_STEEL_CHAR_D;
5701       break;
5702
5703     case 0x164f:        // (green steel)
5704       element = EL_STEEL_CHAR_E;
5705       break;
5706
5707     case 0x1650:        // (green steel)
5708       element = EL_STEEL_CHAR_F;
5709       break;
5710
5711     case 0x1651:        // (green steel)
5712       element = EL_STEEL_CHAR_G;
5713       break;
5714
5715     case 0x1652:        // (green steel)
5716       element = EL_STEEL_CHAR_H;
5717       break;
5718
5719     case 0x1653:        // (green steel)
5720       element = EL_STEEL_CHAR_I;
5721       break;
5722
5723     case 0x1654:        // (green steel)
5724       element = EL_STEEL_CHAR_J;
5725       break;
5726
5727     case 0x1655:        // (green steel)
5728       element = EL_STEEL_CHAR_K;
5729       break;
5730
5731     case 0x1656:        // (green steel)
5732       element = EL_STEEL_CHAR_L;
5733       break;
5734
5735     case 0x1657:        // (green steel)
5736       element = EL_STEEL_CHAR_M;
5737       break;
5738
5739     case 0x1658:        // (green steel)
5740       element = EL_STEEL_CHAR_N;
5741       break;
5742
5743     case 0x1659:        // (green steel)
5744       element = EL_STEEL_CHAR_O;
5745       break;
5746
5747     case 0x165a:        // (green steel)
5748       element = EL_STEEL_CHAR_P;
5749       break;
5750
5751     case 0x165b:        // (green steel)
5752       element = EL_STEEL_CHAR_Q;
5753       break;
5754
5755     case 0x165c:        // (green steel)
5756       element = EL_STEEL_CHAR_R;
5757       break;
5758
5759     case 0x165d:        // (green steel)
5760       element = EL_STEEL_CHAR_S;
5761       break;
5762
5763     case 0x165e:        // (green steel)
5764       element = EL_STEEL_CHAR_T;
5765       break;
5766
5767     case 0x165f:        // (green steel)
5768       element = EL_STEEL_CHAR_U;
5769       break;
5770
5771     case 0x1660:        // (green steel)
5772       element = EL_STEEL_CHAR_V;
5773       break;
5774
5775     case 0x1661:        // (green steel)
5776       element = EL_STEEL_CHAR_W;
5777       break;
5778
5779     case 0x1662:        // (green steel)
5780       element = EL_STEEL_CHAR_X;
5781       break;
5782
5783     case 0x1663:        // (green steel)
5784       element = EL_STEEL_CHAR_Y;
5785       break;
5786
5787     case 0x1664:        // (green steel)
5788       element = EL_STEEL_CHAR_Z;
5789       break;
5790
5791     case 0x1665:        // (green steel)
5792       element = EL_STEEL_CHAR_AUMLAUT;
5793       break;
5794
5795     case 0x1666:        // (green steel)
5796       element = EL_STEEL_CHAR_OUMLAUT;
5797       break;
5798
5799     case 0x1667:        // (green steel)
5800       element = EL_STEEL_CHAR_UUMLAUT;
5801       break;
5802
5803     case 0x1668:        // (green steel)
5804       element = EL_STEEL_CHAR_0;
5805       break;
5806
5807     case 0x1669:        // (green steel)
5808       element = EL_STEEL_CHAR_1;
5809       break;
5810
5811     case 0x166a:        // (green steel)
5812       element = EL_STEEL_CHAR_2;
5813       break;
5814
5815     case 0x166b:        // (green steel)
5816       element = EL_STEEL_CHAR_3;
5817       break;
5818
5819     case 0x166c:        // (green steel)
5820       element = EL_STEEL_CHAR_4;
5821       break;
5822
5823     case 0x166d:        // (green steel)
5824       element = EL_STEEL_CHAR_5;
5825       break;
5826
5827     case 0x166e:        // (green steel)
5828       element = EL_STEEL_CHAR_6;
5829       break;
5830
5831     case 0x166f:        // (green steel)
5832       element = EL_STEEL_CHAR_7;
5833       break;
5834
5835     case 0x1670:        // (green steel)
5836       element = EL_STEEL_CHAR_8;
5837       break;
5838
5839     case 0x1671:        // (green steel)
5840       element = EL_STEEL_CHAR_9;
5841       break;
5842
5843     case 0x1672:        // (green steel)
5844       element = EL_STEEL_CHAR_PERIOD;
5845       break;
5846
5847     case 0x1673:        // (green steel)
5848       element = EL_STEEL_CHAR_EXCLAM;
5849       break;
5850
5851     case 0x1674:        // (green steel)
5852       element = EL_STEEL_CHAR_COLON;
5853       break;
5854
5855     case 0x1675:        // (green steel)
5856       element = EL_STEEL_CHAR_LESS;
5857       break;
5858
5859     case 0x1676:        // (green steel)
5860       element = EL_STEEL_CHAR_GREATER;
5861       break;
5862
5863     case 0x1677:        // (green steel)
5864       element = EL_STEEL_CHAR_QUESTION;
5865       break;
5866
5867     case 0x1678:        // (green steel)
5868       element = EL_STEEL_CHAR_COPYRIGHT;
5869       break;
5870
5871     case 0x1679:        // (green steel)
5872       element = EL_STEEL_CHAR_UP;
5873       break;
5874
5875     case 0x167a:        // (green steel)
5876       element = EL_STEEL_CHAR_DOWN;
5877       break;
5878
5879     case 0x167b:        // (green steel)
5880       element = EL_STEEL_CHAR_BUTTON;
5881       break;
5882
5883     case 0x167c:        // (green steel)
5884       element = EL_STEEL_CHAR_PLUS;
5885       break;
5886
5887     case 0x167d:        // (green steel)
5888       element = EL_STEEL_CHAR_MINUS;
5889       break;
5890
5891     case 0x167e:        // (green steel)
5892       element = EL_STEEL_CHAR_APOSTROPHE;
5893       break;
5894
5895     case 0x167f:        // (green steel)
5896       element = EL_STEEL_CHAR_PARENLEFT;
5897       break;
5898
5899     case 0x1680:        // (green steel)
5900       element = EL_STEEL_CHAR_PARENRIGHT;
5901       break;
5902
5903     case 0x1681:        // gate (red)
5904       element = EL_EM_GATE_1;
5905       break;
5906
5907     case 0x1682:        // secret gate (red)
5908       element = EL_EM_GATE_1_GRAY;
5909       break;
5910
5911     case 0x1683:        // gate (yellow)
5912       element = EL_EM_GATE_2;
5913       break;
5914
5915     case 0x1684:        // secret gate (yellow)
5916       element = EL_EM_GATE_2_GRAY;
5917       break;
5918
5919     case 0x1685:        // gate (blue)
5920       element = EL_EM_GATE_4;
5921       break;
5922
5923     case 0x1686:        // secret gate (blue)
5924       element = EL_EM_GATE_4_GRAY;
5925       break;
5926
5927     case 0x1687:        // gate (green)
5928       element = EL_EM_GATE_3;
5929       break;
5930
5931     case 0x1688:        // secret gate (green)
5932       element = EL_EM_GATE_3_GRAY;
5933       break;
5934
5935     case 0x1689:        // gate (white)
5936       element = EL_DC_GATE_WHITE;
5937       break;
5938
5939     case 0x168a:        // secret gate (white)
5940       element = EL_DC_GATE_WHITE_GRAY;
5941       break;
5942
5943     case 0x168b:        // secret gate (no key)
5944       element = EL_DC_GATE_FAKE_GRAY;
5945       break;
5946
5947     case 0x168c:
5948       element = EL_ROBOT_WHEEL;
5949       break;
5950
5951     case 0x168d:
5952       element = EL_DC_TIMEGATE_SWITCH;
5953       break;
5954
5955     case 0x168e:
5956       element = EL_ACID_POOL_BOTTOM;
5957       break;
5958
5959     case 0x168f:
5960       element = EL_ACID_POOL_TOPLEFT;
5961       break;
5962
5963     case 0x1690:
5964       element = EL_ACID_POOL_TOPRIGHT;
5965       break;
5966
5967     case 0x1691:
5968       element = EL_ACID_POOL_BOTTOMLEFT;
5969       break;
5970
5971     case 0x1692:
5972       element = EL_ACID_POOL_BOTTOMRIGHT;
5973       break;
5974
5975     case 0x1693:
5976       element = EL_STEELWALL;
5977       break;
5978
5979     case 0x1694:
5980       element = EL_STEELWALL_SLIPPERY;
5981       break;
5982
5983     case 0x1695:        // steel wall (not round)
5984       element = EL_STEELWALL;
5985       break;
5986
5987     case 0x1696:        // steel wall (left)
5988       element = EL_DC_STEELWALL_1_LEFT;
5989       break;
5990
5991     case 0x1697:        // steel wall (bottom)
5992       element = EL_DC_STEELWALL_1_BOTTOM;
5993       break;
5994
5995     case 0x1698:        // steel wall (right)
5996       element = EL_DC_STEELWALL_1_RIGHT;
5997       break;
5998
5999     case 0x1699:        // steel wall (top)
6000       element = EL_DC_STEELWALL_1_TOP;
6001       break;
6002
6003     case 0x169a:        // steel wall (left/bottom)
6004       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6005       break;
6006
6007     case 0x169b:        // steel wall (right/bottom)
6008       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6009       break;
6010
6011     case 0x169c:        // steel wall (right/top)
6012       element = EL_DC_STEELWALL_1_TOPRIGHT;
6013       break;
6014
6015     case 0x169d:        // steel wall (left/top)
6016       element = EL_DC_STEELWALL_1_TOPLEFT;
6017       break;
6018
6019     case 0x169e:        // steel wall (right/bottom small)
6020       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6021       break;
6022
6023     case 0x169f:        // steel wall (left/bottom small)
6024       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6025       break;
6026
6027     case 0x16a0:        // steel wall (right/top small)
6028       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6029       break;
6030
6031     case 0x16a1:        // steel wall (left/top small)
6032       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6033       break;
6034
6035     case 0x16a2:        // steel wall (left/right)
6036       element = EL_DC_STEELWALL_1_VERTICAL;
6037       break;
6038
6039     case 0x16a3:        // steel wall (top/bottom)
6040       element = EL_DC_STEELWALL_1_HORIZONTAL;
6041       break;
6042
6043     case 0x16a4:        // steel wall 2 (left end)
6044       element = EL_DC_STEELWALL_2_LEFT;
6045       break;
6046
6047     case 0x16a5:        // steel wall 2 (right end)
6048       element = EL_DC_STEELWALL_2_RIGHT;
6049       break;
6050
6051     case 0x16a6:        // steel wall 2 (top end)
6052       element = EL_DC_STEELWALL_2_TOP;
6053       break;
6054
6055     case 0x16a7:        // steel wall 2 (bottom end)
6056       element = EL_DC_STEELWALL_2_BOTTOM;
6057       break;
6058
6059     case 0x16a8:        // steel wall 2 (left/right)
6060       element = EL_DC_STEELWALL_2_HORIZONTAL;
6061       break;
6062
6063     case 0x16a9:        // steel wall 2 (up/down)
6064       element = EL_DC_STEELWALL_2_VERTICAL;
6065       break;
6066
6067     case 0x16aa:        // steel wall 2 (mid)
6068       element = EL_DC_STEELWALL_2_MIDDLE;
6069       break;
6070
6071     case 0x16ab:
6072       element = EL_SIGN_EXCLAMATION;
6073       break;
6074
6075     case 0x16ac:
6076       element = EL_SIGN_RADIOACTIVITY;
6077       break;
6078
6079     case 0x16ad:
6080       element = EL_SIGN_STOP;
6081       break;
6082
6083     case 0x16ae:
6084       element = EL_SIGN_WHEELCHAIR;
6085       break;
6086
6087     case 0x16af:
6088       element = EL_SIGN_PARKING;
6089       break;
6090
6091     case 0x16b0:
6092       element = EL_SIGN_NO_ENTRY;
6093       break;
6094
6095     case 0x16b1:
6096       element = EL_SIGN_HEART;
6097       break;
6098
6099     case 0x16b2:
6100       element = EL_SIGN_GIVE_WAY;
6101       break;
6102
6103     case 0x16b3:
6104       element = EL_SIGN_ENTRY_FORBIDDEN;
6105       break;
6106
6107     case 0x16b4:
6108       element = EL_SIGN_EMERGENCY_EXIT;
6109       break;
6110
6111     case 0x16b5:
6112       element = EL_SIGN_YIN_YANG;
6113       break;
6114
6115     case 0x16b6:
6116       element = EL_WALL_EMERALD;
6117       break;
6118
6119     case 0x16b7:
6120       element = EL_WALL_DIAMOND;
6121       break;
6122
6123     case 0x16b8:
6124       element = EL_WALL_PEARL;
6125       break;
6126
6127     case 0x16b9:
6128       element = EL_WALL_CRYSTAL;
6129       break;
6130
6131     case 0x16ba:
6132       element = EL_INVISIBLE_WALL;
6133       break;
6134
6135     case 0x16bb:
6136       element = EL_INVISIBLE_STEELWALL;
6137       break;
6138
6139       // 0x16bc - 0x16cb:
6140       // EL_INVISIBLE_SAND
6141
6142     case 0x16cc:
6143       element = EL_LIGHT_SWITCH;
6144       break;
6145
6146     case 0x16cd:
6147       element = EL_ENVELOPE_1;
6148       break;
6149
6150     default:
6151       if (element >= 0x0117 && element <= 0x036e)       // (?)
6152         element = EL_DIAMOND;
6153       else if (element >= 0x042d && element <= 0x0684)  // (?)
6154         element = EL_EMERALD;
6155       else if (element >= 0x157c && element <= 0x158b)
6156         element = EL_SAND;
6157       else if (element >= 0x1590 && element <= 0x159f)
6158         element = EL_DC_LANDMINE;
6159       else if (element >= 0x16bc && element <= 0x16cb)
6160         element = EL_INVISIBLE_SAND;
6161       else
6162       {
6163         Warn("unknown Diamond Caves element 0x%04x", element);
6164
6165         element = EL_UNKNOWN;
6166       }
6167       break;
6168   }
6169
6170   return getMappedElement(element);
6171 }
6172
6173 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6174 {
6175   byte header[DC_LEVEL_HEADER_SIZE];
6176   int envelope_size;
6177   int envelope_header_pos = 62;
6178   int envelope_content_pos = 94;
6179   int level_name_pos = 251;
6180   int level_author_pos = 292;
6181   int envelope_header_len;
6182   int envelope_content_len;
6183   int level_name_len;
6184   int level_author_len;
6185   int fieldx, fieldy;
6186   int num_yamyam_contents;
6187   int i, x, y;
6188
6189   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6190
6191   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6192   {
6193     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6194
6195     header[i * 2 + 0] = header_word >> 8;
6196     header[i * 2 + 1] = header_word & 0xff;
6197   }
6198
6199   // read some values from level header to check level decoding integrity
6200   fieldx = header[6] | (header[7] << 8);
6201   fieldy = header[8] | (header[9] << 8);
6202   num_yamyam_contents = header[60] | (header[61] << 8);
6203
6204   // do some simple sanity checks to ensure that level was correctly decoded
6205   if (fieldx < 1 || fieldx > 256 ||
6206       fieldy < 1 || fieldy > 256 ||
6207       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6208   {
6209     level->no_valid_file = TRUE;
6210
6211     Warn("cannot decode level from stream -- using empty level");
6212
6213     return;
6214   }
6215
6216   // maximum envelope header size is 31 bytes
6217   envelope_header_len   = header[envelope_header_pos];
6218   // maximum envelope content size is 110 (156?) bytes
6219   envelope_content_len  = header[envelope_content_pos];
6220
6221   // maximum level title size is 40 bytes
6222   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6223   // maximum level author size is 30 (51?) bytes
6224   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6225
6226   envelope_size = 0;
6227
6228   for (i = 0; i < envelope_header_len; i++)
6229     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6230       level->envelope[0].text[envelope_size++] =
6231         header[envelope_header_pos + 1 + i];
6232
6233   if (envelope_header_len > 0 && envelope_content_len > 0)
6234   {
6235     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6236       level->envelope[0].text[envelope_size++] = '\n';
6237     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6238       level->envelope[0].text[envelope_size++] = '\n';
6239   }
6240
6241   for (i = 0; i < envelope_content_len; i++)
6242     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6243       level->envelope[0].text[envelope_size++] =
6244         header[envelope_content_pos + 1 + i];
6245
6246   level->envelope[0].text[envelope_size] = '\0';
6247
6248   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6249   level->envelope[0].ysize = 10;
6250   level->envelope[0].autowrap = TRUE;
6251   level->envelope[0].centered = TRUE;
6252
6253   for (i = 0; i < level_name_len; i++)
6254     level->name[i] = header[level_name_pos + 1 + i];
6255   level->name[level_name_len] = '\0';
6256
6257   for (i = 0; i < level_author_len; i++)
6258     level->author[i] = header[level_author_pos + 1 + i];
6259   level->author[level_author_len] = '\0';
6260
6261   num_yamyam_contents = header[60] | (header[61] << 8);
6262   level->num_yamyam_contents =
6263     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6264
6265   for (i = 0; i < num_yamyam_contents; i++)
6266   {
6267     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6268     {
6269       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6270       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6271
6272       if (i < MAX_ELEMENT_CONTENTS)
6273         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6274     }
6275   }
6276
6277   fieldx = header[6] | (header[7] << 8);
6278   fieldy = header[8] | (header[9] << 8);
6279   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6280   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6281
6282   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6283   {
6284     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6285     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6286
6287     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6288       level->field[x][y] = getMappedElement_DC(element_dc);
6289   }
6290
6291   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6292   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6293   level->field[x][y] = EL_PLAYER_1;
6294
6295   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6296   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6297   level->field[x][y] = EL_PLAYER_2;
6298
6299   level->gems_needed            = header[18] | (header[19] << 8);
6300
6301   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6302   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6303   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6304   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6305   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6306   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6307   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6308   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6309   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6310   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6311   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6312   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6313
6314   level->time                   = header[44] | (header[45] << 8);
6315
6316   level->amoeba_speed           = header[46] | (header[47] << 8);
6317   level->time_light             = header[48] | (header[49] << 8);
6318   level->time_timegate          = header[50] | (header[51] << 8);
6319   level->time_wheel             = header[52] | (header[53] << 8);
6320   level->time_magic_wall        = header[54] | (header[55] << 8);
6321   level->extra_time             = header[56] | (header[57] << 8);
6322   level->shield_normal_time     = header[58] | (header[59] << 8);
6323
6324   // shield and extra time elements do not have a score
6325   level->score[SC_SHIELD]       = 0;
6326   level->extra_time_score       = 0;
6327
6328   // set time for normal and deadly shields to the same value
6329   level->shield_deadly_time     = level->shield_normal_time;
6330
6331   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6332   // can slip down from flat walls, like normal walls and steel walls
6333   level->em_slippery_gems = TRUE;
6334
6335   // time score is counted for each 10 seconds left in Diamond Caves levels
6336   level->time_score_base = 10;
6337 }
6338
6339 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6340                                      struct LevelFileInfo *level_file_info,
6341                                      boolean level_info_only)
6342 {
6343   char *filename = level_file_info->filename;
6344   File *file;
6345   int num_magic_bytes = 8;
6346   char magic_bytes[num_magic_bytes + 1];
6347   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6348
6349   if (!(file = openFile(filename, MODE_READ)))
6350   {
6351     level->no_valid_file = TRUE;
6352
6353     if (!level_info_only)
6354       Warn("cannot read level '%s' -- using empty level", filename);
6355
6356     return;
6357   }
6358
6359   // fseek(file, 0x0000, SEEK_SET);
6360
6361   if (level_file_info->packed)
6362   {
6363     // read "magic bytes" from start of file
6364     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6365       magic_bytes[0] = '\0';
6366
6367     // check "magic bytes" for correct file format
6368     if (!strPrefix(magic_bytes, "DC2"))
6369     {
6370       level->no_valid_file = TRUE;
6371
6372       Warn("unknown DC level file '%s' -- using empty level", filename);
6373
6374       return;
6375     }
6376
6377     if (strPrefix(magic_bytes, "DC2Win95") ||
6378         strPrefix(magic_bytes, "DC2Win98"))
6379     {
6380       int position_first_level = 0x00fa;
6381       int extra_bytes = 4;
6382       int skip_bytes;
6383
6384       // advance file stream to first level inside the level package
6385       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6386
6387       // each block of level data is followed by block of non-level data
6388       num_levels_to_skip *= 2;
6389
6390       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6391       while (num_levels_to_skip >= 0)
6392       {
6393         // advance file stream to next level inside the level package
6394         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6395         {
6396           level->no_valid_file = TRUE;
6397
6398           Warn("cannot fseek in file '%s' -- using empty level", filename);
6399
6400           return;
6401         }
6402
6403         // skip apparently unused extra bytes following each level
6404         ReadUnusedBytesFromFile(file, extra_bytes);
6405
6406         // read size of next level in level package
6407         skip_bytes = getFile32BitLE(file);
6408
6409         num_levels_to_skip--;
6410       }
6411     }
6412     else
6413     {
6414       level->no_valid_file = TRUE;
6415
6416       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6417
6418       return;
6419     }
6420   }
6421
6422   LoadLevelFromFileStream_DC(file, level);
6423
6424   closeFile(file);
6425 }
6426
6427
6428 // ----------------------------------------------------------------------------
6429 // functions for loading SB level
6430 // ----------------------------------------------------------------------------
6431
6432 int getMappedElement_SB(int element_ascii, boolean use_ces)
6433 {
6434   static struct
6435   {
6436     int ascii;
6437     int sb;
6438     int ce;
6439   }
6440   sb_element_mapping[] =
6441   {
6442     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6443     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6444     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6445     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6446     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6447     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6448     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6449     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6450
6451     { 0,   -1,                      -1          },
6452   };
6453
6454   int i;
6455
6456   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6457     if (element_ascii == sb_element_mapping[i].ascii)
6458       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6459
6460   return EL_UNDEFINED;
6461 }
6462
6463 static void SetLevelSettings_SB(struct LevelInfo *level)
6464 {
6465   // time settings
6466   level->time = 0;
6467   level->use_step_counter = TRUE;
6468
6469   // score settings
6470   level->score[SC_TIME_BONUS] = 0;
6471   level->time_score_base = 1;
6472   level->rate_time_over_score = TRUE;
6473
6474   // game settings
6475   level->auto_exit_sokoban = TRUE;
6476 }
6477
6478 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6479                                      struct LevelFileInfo *level_file_info,
6480                                      boolean level_info_only)
6481 {
6482   char *filename = level_file_info->filename;
6483   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6484   char last_comment[MAX_LINE_LEN];
6485   char level_name[MAX_LINE_LEN];
6486   char *line_ptr;
6487   File *file;
6488   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6489   boolean read_continued_line = FALSE;
6490   boolean reading_playfield = FALSE;
6491   boolean got_valid_playfield_line = FALSE;
6492   boolean invalid_playfield_char = FALSE;
6493   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6494   int file_level_nr = 0;
6495   int x = 0, y = 0;             // initialized to make compilers happy
6496
6497   last_comment[0] = '\0';
6498   level_name[0] = '\0';
6499
6500   if (!(file = openFile(filename, MODE_READ)))
6501   {
6502     level->no_valid_file = TRUE;
6503
6504     if (!level_info_only)
6505       Warn("cannot read level '%s' -- using empty level", filename);
6506
6507     return;
6508   }
6509
6510   while (!checkEndOfFile(file))
6511   {
6512     // level successfully read, but next level may follow here
6513     if (!got_valid_playfield_line && reading_playfield)
6514     {
6515       // read playfield from single level file -- skip remaining file
6516       if (!level_file_info->packed)
6517         break;
6518
6519       if (file_level_nr >= num_levels_to_skip)
6520         break;
6521
6522       file_level_nr++;
6523
6524       last_comment[0] = '\0';
6525       level_name[0] = '\0';
6526
6527       reading_playfield = FALSE;
6528     }
6529
6530     got_valid_playfield_line = FALSE;
6531
6532     // read next line of input file
6533     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6534       break;
6535
6536     // cut trailing line break (this can be newline and/or carriage return)
6537     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6538       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6539         *line_ptr = '\0';
6540
6541     // copy raw input line for later use (mainly debugging output)
6542     strcpy(line_raw, line);
6543
6544     if (read_continued_line)
6545     {
6546       // append new line to existing line, if there is enough space
6547       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6548         strcat(previous_line, line_ptr);
6549
6550       strcpy(line, previous_line);      // copy storage buffer to line
6551
6552       read_continued_line = FALSE;
6553     }
6554
6555     // if the last character is '\', continue at next line
6556     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6557     {
6558       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6559       strcpy(previous_line, line);      // copy line to storage buffer
6560
6561       read_continued_line = TRUE;
6562
6563       continue;
6564     }
6565
6566     // skip empty lines
6567     if (line[0] == '\0')
6568       continue;
6569
6570     // extract comment text from comment line
6571     if (line[0] == ';')
6572     {
6573       for (line_ptr = line; *line_ptr; line_ptr++)
6574         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6575           break;
6576
6577       strcpy(last_comment, line_ptr);
6578
6579       continue;
6580     }
6581
6582     // extract level title text from line containing level title
6583     if (line[0] == '\'')
6584     {
6585       strcpy(level_name, &line[1]);
6586
6587       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6588         level_name[strlen(level_name) - 1] = '\0';
6589
6590       continue;
6591     }
6592
6593     // skip lines containing only spaces (or empty lines)
6594     for (line_ptr = line; *line_ptr; line_ptr++)
6595       if (*line_ptr != ' ')
6596         break;
6597     if (*line_ptr == '\0')
6598       continue;
6599
6600     // at this point, we have found a line containing part of a playfield
6601
6602     got_valid_playfield_line = TRUE;
6603
6604     if (!reading_playfield)
6605     {
6606       reading_playfield = TRUE;
6607       invalid_playfield_char = FALSE;
6608
6609       for (x = 0; x < MAX_LEV_FIELDX; x++)
6610         for (y = 0; y < MAX_LEV_FIELDY; y++)
6611           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6612
6613       level->fieldx = 0;
6614       level->fieldy = 0;
6615
6616       // start with topmost tile row
6617       y = 0;
6618     }
6619
6620     // skip playfield line if larger row than allowed
6621     if (y >= MAX_LEV_FIELDY)
6622       continue;
6623
6624     // start with leftmost tile column
6625     x = 0;
6626
6627     // read playfield elements from line
6628     for (line_ptr = line; *line_ptr; line_ptr++)
6629     {
6630       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6631
6632       // stop parsing playfield line if larger column than allowed
6633       if (x >= MAX_LEV_FIELDX)
6634         break;
6635
6636       if (mapped_sb_element == EL_UNDEFINED)
6637       {
6638         invalid_playfield_char = TRUE;
6639
6640         break;
6641       }
6642
6643       level->field[x][y] = mapped_sb_element;
6644
6645       // continue with next tile column
6646       x++;
6647
6648       level->fieldx = MAX(x, level->fieldx);
6649     }
6650
6651     if (invalid_playfield_char)
6652     {
6653       // if first playfield line, treat invalid lines as comment lines
6654       if (y == 0)
6655         reading_playfield = FALSE;
6656
6657       continue;
6658     }
6659
6660     // continue with next tile row
6661     y++;
6662   }
6663
6664   closeFile(file);
6665
6666   level->fieldy = y;
6667
6668   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6669   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6670
6671   if (!reading_playfield)
6672   {
6673     level->no_valid_file = TRUE;
6674
6675     Warn("cannot read level '%s' -- using empty level", filename);
6676
6677     return;
6678   }
6679
6680   if (*level_name != '\0')
6681   {
6682     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6683     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6684   }
6685   else if (*last_comment != '\0')
6686   {
6687     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6688     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6689   }
6690   else
6691   {
6692     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6693   }
6694
6695   // set all empty fields beyond the border walls to invisible steel wall
6696   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6697   {
6698     if ((x == 0 || x == level->fieldx - 1 ||
6699          y == 0 || y == level->fieldy - 1) &&
6700         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6701       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6702                      level->field, level->fieldx, level->fieldy);
6703   }
6704
6705   // set special level settings for Sokoban levels
6706   SetLevelSettings_SB(level);
6707
6708   if (load_xsb_to_ces)
6709   {
6710     // special global settings can now be set in level template
6711     level->use_custom_template = TRUE;
6712   }
6713 }
6714
6715
6716 // -------------------------------------------------------------------------
6717 // functions for handling native levels
6718 // -------------------------------------------------------------------------
6719
6720 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6721                                      struct LevelFileInfo *level_file_info,
6722                                      boolean level_info_only)
6723 {
6724   int pos = 0;
6725
6726   // determine position of requested level inside level package
6727   if (level_file_info->packed)
6728     pos = level_file_info->nr - leveldir_current->first_level;
6729
6730   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6731     level->no_valid_file = TRUE;
6732 }
6733
6734 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6735                                      struct LevelFileInfo *level_file_info,
6736                                      boolean level_info_only)
6737 {
6738   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6739     level->no_valid_file = TRUE;
6740 }
6741
6742 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6743                                      struct LevelFileInfo *level_file_info,
6744                                      boolean level_info_only)
6745 {
6746   int pos = 0;
6747
6748   // determine position of requested level inside level package
6749   if (level_file_info->packed)
6750     pos = level_file_info->nr - leveldir_current->first_level;
6751
6752   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6753     level->no_valid_file = TRUE;
6754 }
6755
6756 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6757                                      struct LevelFileInfo *level_file_info,
6758                                      boolean level_info_only)
6759 {
6760   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6761     level->no_valid_file = TRUE;
6762 }
6763
6764 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6765 {
6766   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6767     CopyNativeLevel_RND_to_BD(level);
6768   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6769     CopyNativeLevel_RND_to_EM(level);
6770   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6771     CopyNativeLevel_RND_to_SP(level);
6772   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6773     CopyNativeLevel_RND_to_MM(level);
6774 }
6775
6776 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6777 {
6778   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6779     CopyNativeLevel_BD_to_RND(level);
6780   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6781     CopyNativeLevel_EM_to_RND(level);
6782   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6783     CopyNativeLevel_SP_to_RND(level);
6784   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6785     CopyNativeLevel_MM_to_RND(level);
6786 }
6787
6788 void SaveNativeLevel(struct LevelInfo *level)
6789 {
6790   // saving native level files only supported for some game engines
6791   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6792       level->game_engine_type != GAME_ENGINE_TYPE_SP)
6793     return;
6794
6795   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6796                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6797   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6798   char *filename = getLevelFilenameFromBasename(basename);
6799
6800   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6801     return;
6802
6803   boolean success = FALSE;
6804
6805   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6806   {
6807     CopyNativeLevel_RND_to_BD(level);
6808     // CopyNativeTape_RND_to_BD(level);
6809
6810     success = SaveNativeLevel_BD(filename);
6811   }
6812   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6813   {
6814     CopyNativeLevel_RND_to_SP(level);
6815     CopyNativeTape_RND_to_SP(level);
6816
6817     success = SaveNativeLevel_SP(filename);
6818   }
6819
6820   if (success)
6821     Request("Native level file saved!", REQ_CONFIRM);
6822   else
6823     Request("Failed to save native level file!", REQ_CONFIRM);
6824 }
6825
6826
6827 // ----------------------------------------------------------------------------
6828 // functions for loading generic level
6829 // ----------------------------------------------------------------------------
6830
6831 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6832                                   struct LevelFileInfo *level_file_info,
6833                                   boolean level_info_only)
6834 {
6835   // always start with reliable default values
6836   setLevelInfoToDefaults(level, level_info_only, TRUE);
6837
6838   switch (level_file_info->type)
6839   {
6840     case LEVEL_FILE_TYPE_RND:
6841       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6842       break;
6843
6844     case LEVEL_FILE_TYPE_BD:
6845       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6846       level->game_engine_type = GAME_ENGINE_TYPE_BD;
6847       break;
6848
6849     case LEVEL_FILE_TYPE_EM:
6850       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6851       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6852       break;
6853
6854     case LEVEL_FILE_TYPE_SP:
6855       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6856       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6857       break;
6858
6859     case LEVEL_FILE_TYPE_MM:
6860       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6861       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6862       break;
6863
6864     case LEVEL_FILE_TYPE_DC:
6865       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6866       break;
6867
6868     case LEVEL_FILE_TYPE_SB:
6869       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6870       break;
6871
6872     default:
6873       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6874       break;
6875   }
6876
6877   // if level file is invalid, restore level structure to default values
6878   if (level->no_valid_file)
6879     setLevelInfoToDefaults(level, level_info_only, FALSE);
6880
6881   if (check_special_flags("use_native_bd_game_engine"))
6882     level->game_engine_type = GAME_ENGINE_TYPE_BD;
6883
6884   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6885     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6886
6887   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6888     CopyNativeLevel_Native_to_RND(level);
6889 }
6890
6891 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6892 {
6893   static struct LevelFileInfo level_file_info;
6894
6895   // always start with reliable default values
6896   setFileInfoToDefaults(&level_file_info);
6897
6898   level_file_info.nr = 0;                       // unknown level number
6899   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
6900
6901   setString(&level_file_info.filename, filename);
6902
6903   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6904 }
6905
6906 static void LoadLevel_InitVersion(struct LevelInfo *level)
6907 {
6908   int i, j;
6909
6910   if (leveldir_current == NULL)         // only when dumping level
6911     return;
6912
6913   // all engine modifications also valid for levels which use latest engine
6914   if (level->game_version < VERSION_IDENT(3,2,0,5))
6915   {
6916     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6917     level->time_score_base = 10;
6918   }
6919
6920   if (leveldir_current->latest_engine)
6921   {
6922     // ---------- use latest game engine --------------------------------------
6923
6924     /* For all levels which are forced to use the latest game engine version
6925        (normally all but user contributed, private and undefined levels), set
6926        the game engine version to the actual version; this allows for actual
6927        corrections in the game engine to take effect for existing, converted
6928        levels (from "classic" or other existing games) to make the emulation
6929        of the corresponding game more accurate, while (hopefully) not breaking
6930        existing levels created from other players. */
6931
6932     level->game_version = GAME_VERSION_ACTUAL;
6933
6934     /* Set special EM style gems behaviour: EM style gems slip down from
6935        normal, steel and growing wall. As this is a more fundamental change,
6936        it seems better to set the default behaviour to "off" (as it is more
6937        natural) and make it configurable in the level editor (as a property
6938        of gem style elements). Already existing converted levels (neither
6939        private nor contributed levels) are changed to the new behaviour. */
6940
6941     if (level->file_version < FILE_VERSION_2_0)
6942       level->em_slippery_gems = TRUE;
6943
6944     return;
6945   }
6946
6947   // ---------- use game engine the level was created with --------------------
6948
6949   /* For all levels which are not forced to use the latest game engine
6950      version (normally user contributed, private and undefined levels),
6951      use the version of the game engine the levels were created for.
6952
6953      Since 2.0.1, the game engine version is now directly stored
6954      in the level file (chunk "VERS"), so there is no need anymore
6955      to set the game version from the file version (except for old,
6956      pre-2.0 levels, where the game version is still taken from the
6957      file format version used to store the level -- see above). */
6958
6959   // player was faster than enemies in 1.0.0 and before
6960   if (level->file_version == FILE_VERSION_1_0)
6961     for (i = 0; i < MAX_PLAYERS; i++)
6962       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6963
6964   // default behaviour for EM style gems was "slippery" only in 2.0.1
6965   if (level->game_version == VERSION_IDENT(2,0,1,0))
6966     level->em_slippery_gems = TRUE;
6967
6968   // springs could be pushed over pits before (pre-release version) 2.2.0
6969   if (level->game_version < VERSION_IDENT(2,2,0,0))
6970     level->use_spring_bug = TRUE;
6971
6972   if (level->game_version < VERSION_IDENT(3,2,0,5))
6973   {
6974     // time orb caused limited time in endless time levels before 3.2.0-5
6975     level->use_time_orb_bug = TRUE;
6976
6977     // default behaviour for snapping was "no snap delay" before 3.2.0-5
6978     level->block_snap_field = FALSE;
6979
6980     // extra time score was same value as time left score before 3.2.0-5
6981     level->extra_time_score = level->score[SC_TIME_BONUS];
6982   }
6983
6984   if (level->game_version < VERSION_IDENT(3,2,0,7))
6985   {
6986     // default behaviour for snapping was "not continuous" before 3.2.0-7
6987     level->continuous_snapping = FALSE;
6988   }
6989
6990   // only few elements were able to actively move into acid before 3.1.0
6991   // trigger settings did not exist before 3.1.0; set to default "any"
6992   if (level->game_version < VERSION_IDENT(3,1,0,0))
6993   {
6994     // correct "can move into acid" settings (all zero in old levels)
6995
6996     level->can_move_into_acid_bits = 0; // nothing can move into acid
6997     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6998
6999     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7000     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7001     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7002     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7003
7004     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7005       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7006
7007     // correct trigger settings (stored as zero == "none" in old levels)
7008
7009     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7010     {
7011       int element = EL_CUSTOM_START + i;
7012       struct ElementInfo *ei = &element_info[element];
7013
7014       for (j = 0; j < ei->num_change_pages; j++)
7015       {
7016         struct ElementChangeInfo *change = &ei->change_page[j];
7017
7018         change->trigger_player = CH_PLAYER_ANY;
7019         change->trigger_page = CH_PAGE_ANY;
7020       }
7021     }
7022   }
7023
7024   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7025   {
7026     int element = EL_CUSTOM_256;
7027     struct ElementInfo *ei = &element_info[element];
7028     struct ElementChangeInfo *change = &ei->change_page[0];
7029
7030     /* This is needed to fix a problem that was caused by a bugfix in function
7031        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7032        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7033        not replace walkable elements, but instead just placed the player on it,
7034        without placing the Sokoban field under the player). Unfortunately, this
7035        breaks "Snake Bite" style levels when the snake is halfway through a door
7036        that just closes (the snake head is still alive and can be moved in this
7037        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7038        player (without Sokoban element) which then gets killed as designed). */
7039
7040     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7041          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7042         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7043       change->target_element = EL_PLAYER_1;
7044   }
7045
7046   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7047   if (level->game_version < VERSION_IDENT(3,2,5,0))
7048   {
7049     /* This is needed to fix a problem that was caused by a bugfix in function
7050        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7051        corrects the behaviour when a custom element changes to another custom
7052        element with a higher element number that has change actions defined.
7053        Normally, only one change per frame is allowed for custom elements.
7054        Therefore, it is checked if a custom element already changed in the
7055        current frame; if it did, subsequent changes are suppressed.
7056        Unfortunately, this is only checked for element changes, but not for
7057        change actions, which are still executed. As the function above loops
7058        through all custom elements from lower to higher, an element change
7059        resulting in a lower CE number won't be checked again, while a target
7060        element with a higher number will also be checked, and potential change
7061        actions will get executed for this CE, too (which is wrong), while
7062        further changes are ignored (which is correct). As this bugfix breaks
7063        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7064        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7065        behaviour for existing levels and tapes that make use of this bug */
7066
7067     level->use_action_after_change_bug = TRUE;
7068   }
7069
7070   // not centering level after relocating player was default only in 3.2.3
7071   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7072     level->shifted_relocation = TRUE;
7073
7074   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7075   if (level->game_version < VERSION_IDENT(3,2,6,0))
7076     level->em_explodes_by_fire = TRUE;
7077
7078   // levels were solved by the first player entering an exit up to 4.1.0.0
7079   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7080     level->solved_by_one_player = TRUE;
7081
7082   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7083   if (level->game_version < VERSION_IDENT(4,1,1,1))
7084     level->use_life_bugs = TRUE;
7085
7086   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7087   if (level->game_version < VERSION_IDENT(4,1,1,1))
7088     level->sb_objects_needed = FALSE;
7089
7090   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7091   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7092     level->finish_dig_collect = FALSE;
7093
7094   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7095   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7096     level->keep_walkable_ce = TRUE;
7097 }
7098
7099 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7100 {
7101   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7102   int x, y;
7103
7104   // check if this level is (not) a Sokoban level
7105   for (y = 0; y < level->fieldy; y++)
7106     for (x = 0; x < level->fieldx; x++)
7107       if (!IS_SB_ELEMENT(Tile[x][y]))
7108         is_sokoban_level = FALSE;
7109
7110   if (is_sokoban_level)
7111   {
7112     // set special level settings for Sokoban levels
7113     SetLevelSettings_SB(level);
7114   }
7115 }
7116
7117 static void LoadLevel_InitSettings(struct LevelInfo *level)
7118 {
7119   // adjust level settings for (non-native) Sokoban-style levels
7120   LoadLevel_InitSettings_SB(level);
7121
7122   // rename levels with title "nameless level" or if renaming is forced
7123   if (leveldir_current->empty_level_name != NULL &&
7124       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7125        leveldir_current->force_level_name))
7126     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7127              leveldir_current->empty_level_name, level_nr);
7128 }
7129
7130 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7131 {
7132   int i, x, y;
7133
7134   // map elements that have changed in newer versions
7135   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7136                                                     level->game_version);
7137   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7138     for (x = 0; x < 3; x++)
7139       for (y = 0; y < 3; y++)
7140         level->yamyam_content[i].e[x][y] =
7141           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7142                                     level->game_version);
7143
7144 }
7145
7146 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7147 {
7148   int i, j;
7149
7150   // map custom element change events that have changed in newer versions
7151   // (these following values were accidentally changed in version 3.0.1)
7152   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7153   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7154   {
7155     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7156     {
7157       int element = EL_CUSTOM_START + i;
7158
7159       // order of checking and copying events to be mapped is important
7160       // (do not change the start and end value -- they are constant)
7161       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7162       {
7163         if (HAS_CHANGE_EVENT(element, j - 2))
7164         {
7165           SET_CHANGE_EVENT(element, j - 2, FALSE);
7166           SET_CHANGE_EVENT(element, j, TRUE);
7167         }
7168       }
7169
7170       // order of checking and copying events to be mapped is important
7171       // (do not change the start and end value -- they are constant)
7172       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7173       {
7174         if (HAS_CHANGE_EVENT(element, j - 1))
7175         {
7176           SET_CHANGE_EVENT(element, j - 1, FALSE);
7177           SET_CHANGE_EVENT(element, j, TRUE);
7178         }
7179       }
7180     }
7181   }
7182
7183   // initialize "can_change" field for old levels with only one change page
7184   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7185   {
7186     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7187     {
7188       int element = EL_CUSTOM_START + i;
7189
7190       if (CAN_CHANGE(element))
7191         element_info[element].change->can_change = TRUE;
7192     }
7193   }
7194
7195   // correct custom element values (for old levels without these options)
7196   if (level->game_version < VERSION_IDENT(3,1,1,0))
7197   {
7198     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7199     {
7200       int element = EL_CUSTOM_START + i;
7201       struct ElementInfo *ei = &element_info[element];
7202
7203       if (ei->access_direction == MV_NO_DIRECTION)
7204         ei->access_direction = MV_ALL_DIRECTIONS;
7205     }
7206   }
7207
7208   // correct custom element values (fix invalid values for all versions)
7209   if (1)
7210   {
7211     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7212     {
7213       int element = EL_CUSTOM_START + i;
7214       struct ElementInfo *ei = &element_info[element];
7215
7216       for (j = 0; j < ei->num_change_pages; j++)
7217       {
7218         struct ElementChangeInfo *change = &ei->change_page[j];
7219
7220         if (change->trigger_player == CH_PLAYER_NONE)
7221           change->trigger_player = CH_PLAYER_ANY;
7222
7223         if (change->trigger_side == CH_SIDE_NONE)
7224           change->trigger_side = CH_SIDE_ANY;
7225       }
7226     }
7227   }
7228
7229   // initialize "can_explode" field for old levels which did not store this
7230   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7231   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7232   {
7233     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7234     {
7235       int element = EL_CUSTOM_START + i;
7236
7237       if (EXPLODES_1X1_OLD(element))
7238         element_info[element].explosion_type = EXPLODES_1X1;
7239
7240       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7241                                              EXPLODES_SMASHED(element) ||
7242                                              EXPLODES_IMPACT(element)));
7243     }
7244   }
7245
7246   // correct previously hard-coded move delay values for maze runner style
7247   if (level->game_version < VERSION_IDENT(3,1,1,0))
7248   {
7249     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7250     {
7251       int element = EL_CUSTOM_START + i;
7252
7253       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7254       {
7255         // previously hard-coded and therefore ignored
7256         element_info[element].move_delay_fixed = 9;
7257         element_info[element].move_delay_random = 0;
7258       }
7259     }
7260   }
7261
7262   // set some other uninitialized values of custom elements in older levels
7263   if (level->game_version < VERSION_IDENT(3,1,0,0))
7264   {
7265     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7266     {
7267       int element = EL_CUSTOM_START + i;
7268
7269       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7270
7271       element_info[element].explosion_delay = 17;
7272       element_info[element].ignition_delay = 8;
7273     }
7274   }
7275
7276   // set mouse click change events to work for left/middle/right mouse button
7277   if (level->game_version < VERSION_IDENT(4,2,3,0))
7278   {
7279     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7280     {
7281       int element = EL_CUSTOM_START + i;
7282       struct ElementInfo *ei = &element_info[element];
7283
7284       for (j = 0; j < ei->num_change_pages; j++)
7285       {
7286         struct ElementChangeInfo *change = &ei->change_page[j];
7287
7288         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7289             change->has_event[CE_PRESSED_BY_MOUSE] ||
7290             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7291             change->has_event[CE_MOUSE_PRESSED_ON_X])
7292           change->trigger_side = CH_SIDE_ANY;
7293       }
7294     }
7295   }
7296 }
7297
7298 static void LoadLevel_InitElements(struct LevelInfo *level)
7299 {
7300   LoadLevel_InitStandardElements(level);
7301
7302   if (level->file_has_custom_elements)
7303     LoadLevel_InitCustomElements(level);
7304
7305   // initialize element properties for level editor etc.
7306   InitElementPropertiesEngine(level->game_version);
7307   InitElementPropertiesGfxElement();
7308 }
7309
7310 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7311 {
7312   int x, y;
7313
7314   // map elements that have changed in newer versions
7315   for (y = 0; y < level->fieldy; y++)
7316     for (x = 0; x < level->fieldx; x++)
7317       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7318                                                      level->game_version);
7319
7320   // clear unused playfield data (nicer if level gets resized in editor)
7321   for (x = 0; x < MAX_LEV_FIELDX; x++)
7322     for (y = 0; y < MAX_LEV_FIELDY; y++)
7323       if (x >= level->fieldx || y >= level->fieldy)
7324         level->field[x][y] = EL_EMPTY;
7325
7326   // copy elements to runtime playfield array
7327   for (x = 0; x < MAX_LEV_FIELDX; x++)
7328     for (y = 0; y < MAX_LEV_FIELDY; y++)
7329       Tile[x][y] = level->field[x][y];
7330
7331   // initialize level size variables for faster access
7332   lev_fieldx = level->fieldx;
7333   lev_fieldy = level->fieldy;
7334
7335   // determine border element for this level
7336   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7337     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7338   else
7339     SetBorderElement();
7340 }
7341
7342 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7343 {
7344   struct LevelFileInfo *level_file_info = &level->file_info;
7345
7346   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7347     CopyNativeLevel_RND_to_Native(level);
7348 }
7349
7350 static void LoadLevelTemplate_LoadAndInit(void)
7351 {
7352   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7353
7354   LoadLevel_InitVersion(&level_template);
7355   LoadLevel_InitElements(&level_template);
7356   LoadLevel_InitSettings(&level_template);
7357
7358   ActivateLevelTemplate();
7359 }
7360
7361 void LoadLevelTemplate(int nr)
7362 {
7363   if (!fileExists(getGlobalLevelTemplateFilename()))
7364   {
7365     Warn("no level template found for this level");
7366
7367     return;
7368   }
7369
7370   setLevelFileInfo(&level_template.file_info, nr);
7371
7372   LoadLevelTemplate_LoadAndInit();
7373 }
7374
7375 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7376 {
7377   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7378
7379   LoadLevelTemplate_LoadAndInit();
7380 }
7381
7382 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7383 {
7384   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7385
7386   if (level.use_custom_template)
7387   {
7388     if (network_level != NULL)
7389       LoadNetworkLevelTemplate(network_level);
7390     else
7391       LoadLevelTemplate(-1);
7392   }
7393
7394   LoadLevel_InitVersion(&level);
7395   LoadLevel_InitElements(&level);
7396   LoadLevel_InitPlayfield(&level);
7397   LoadLevel_InitSettings(&level);
7398
7399   LoadLevel_InitNativeEngines(&level);
7400 }
7401
7402 void LoadLevel(int nr)
7403 {
7404   SetLevelSetInfo(leveldir_current->identifier, nr);
7405
7406   setLevelFileInfo(&level.file_info, nr);
7407
7408   LoadLevel_LoadAndInit(NULL);
7409 }
7410
7411 void LoadLevelInfoOnly(int nr)
7412 {
7413   setLevelFileInfo(&level.file_info, nr);
7414
7415   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7416 }
7417
7418 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7419 {
7420   SetLevelSetInfo(network_level->leveldir_identifier,
7421                   network_level->file_info.nr);
7422
7423   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7424
7425   LoadLevel_LoadAndInit(network_level);
7426 }
7427
7428 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7429 {
7430   int chunk_size = 0;
7431
7432   chunk_size += putFileVersion(file, level->file_version);
7433   chunk_size += putFileVersion(file, level->game_version);
7434
7435   return chunk_size;
7436 }
7437
7438 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7439 {
7440   int chunk_size = 0;
7441
7442   chunk_size += putFile16BitBE(file, level->creation_date.year);
7443   chunk_size += putFile8Bit(file,    level->creation_date.month);
7444   chunk_size += putFile8Bit(file,    level->creation_date.day);
7445
7446   return chunk_size;
7447 }
7448
7449 #if ENABLE_HISTORIC_CHUNKS
7450 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7451 {
7452   int i, x, y;
7453
7454   putFile8Bit(file, level->fieldx);
7455   putFile8Bit(file, level->fieldy);
7456
7457   putFile16BitBE(file, level->time);
7458   putFile16BitBE(file, level->gems_needed);
7459
7460   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7461     putFile8Bit(file, level->name[i]);
7462
7463   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7464     putFile8Bit(file, level->score[i]);
7465
7466   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7467     for (y = 0; y < 3; y++)
7468       for (x = 0; x < 3; x++)
7469         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7470                            level->yamyam_content[i].e[x][y]));
7471   putFile8Bit(file, level->amoeba_speed);
7472   putFile8Bit(file, level->time_magic_wall);
7473   putFile8Bit(file, level->time_wheel);
7474   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7475                      level->amoeba_content));
7476   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7477   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7478   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7479   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7480
7481   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7482
7483   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7484   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7485   putFile32BitBE(file, level->can_move_into_acid_bits);
7486   putFile8Bit(file, level->dont_collide_with_bits);
7487
7488   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7489   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7490
7491   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7492   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7493   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7494
7495   putFile8Bit(file, level->game_engine_type);
7496
7497   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7498 }
7499 #endif
7500
7501 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7502 {
7503   int chunk_size = 0;
7504   int i;
7505
7506   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7507     chunk_size += putFile8Bit(file, level->name[i]);
7508
7509   return chunk_size;
7510 }
7511
7512 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7513 {
7514   int chunk_size = 0;
7515   int i;
7516
7517   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7518     chunk_size += putFile8Bit(file, level->author[i]);
7519
7520   return chunk_size;
7521 }
7522
7523 #if ENABLE_HISTORIC_CHUNKS
7524 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7525 {
7526   int chunk_size = 0;
7527   int x, y;
7528
7529   for (y = 0; y < level->fieldy; y++)
7530     for (x = 0; x < level->fieldx; x++)
7531       if (level->encoding_16bit_field)
7532         chunk_size += putFile16BitBE(file, level->field[x][y]);
7533       else
7534         chunk_size += putFile8Bit(file, level->field[x][y]);
7535
7536   return chunk_size;
7537 }
7538 #endif
7539
7540 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7541 {
7542   int chunk_size = 0;
7543   int x, y;
7544
7545   for (y = 0; y < level->fieldy; y++) 
7546     for (x = 0; x < level->fieldx; x++) 
7547       chunk_size += putFile16BitBE(file, level->field[x][y]);
7548
7549   return chunk_size;
7550 }
7551
7552 #if ENABLE_HISTORIC_CHUNKS
7553 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7554 {
7555   int i, x, y;
7556
7557   putFile8Bit(file, EL_YAMYAM);
7558   putFile8Bit(file, level->num_yamyam_contents);
7559   putFile8Bit(file, 0);
7560   putFile8Bit(file, 0);
7561
7562   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7563     for (y = 0; y < 3; y++)
7564       for (x = 0; x < 3; x++)
7565         if (level->encoding_16bit_field)
7566           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7567         else
7568           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7569 }
7570 #endif
7571
7572 #if ENABLE_HISTORIC_CHUNKS
7573 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7574 {
7575   int i, x, y;
7576   int num_contents, content_xsize, content_ysize;
7577   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7578
7579   if (element == EL_YAMYAM)
7580   {
7581     num_contents = level->num_yamyam_contents;
7582     content_xsize = 3;
7583     content_ysize = 3;
7584
7585     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7586       for (y = 0; y < 3; y++)
7587         for (x = 0; x < 3; x++)
7588           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7589   }
7590   else if (element == EL_BD_AMOEBA)
7591   {
7592     num_contents = 1;
7593     content_xsize = 1;
7594     content_ysize = 1;
7595
7596     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7597       for (y = 0; y < 3; y++)
7598         for (x = 0; x < 3; x++)
7599           content_array[i][x][y] = EL_EMPTY;
7600     content_array[0][0][0] = level->amoeba_content;
7601   }
7602   else
7603   {
7604     // chunk header already written -- write empty chunk data
7605     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7606
7607     Warn("cannot save content for element '%d'", element);
7608
7609     return;
7610   }
7611
7612   putFile16BitBE(file, element);
7613   putFile8Bit(file, num_contents);
7614   putFile8Bit(file, content_xsize);
7615   putFile8Bit(file, content_ysize);
7616
7617   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7618
7619   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7620     for (y = 0; y < 3; y++)
7621       for (x = 0; x < 3; x++)
7622         putFile16BitBE(file, content_array[i][x][y]);
7623 }
7624 #endif
7625
7626 #if ENABLE_HISTORIC_CHUNKS
7627 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7628 {
7629   int envelope_nr = element - EL_ENVELOPE_1;
7630   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7631   int chunk_size = 0;
7632   int i;
7633
7634   chunk_size += putFile16BitBE(file, element);
7635   chunk_size += putFile16BitBE(file, envelope_len);
7636   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7637   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7638
7639   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7640   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7641
7642   for (i = 0; i < envelope_len; i++)
7643     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7644
7645   return chunk_size;
7646 }
7647 #endif
7648
7649 #if ENABLE_HISTORIC_CHUNKS
7650 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7651                            int num_changed_custom_elements)
7652 {
7653   int i, check = 0;
7654
7655   putFile16BitBE(file, num_changed_custom_elements);
7656
7657   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7658   {
7659     int element = EL_CUSTOM_START + i;
7660
7661     struct ElementInfo *ei = &element_info[element];
7662
7663     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7664     {
7665       if (check < num_changed_custom_elements)
7666       {
7667         putFile16BitBE(file, element);
7668         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7669       }
7670
7671       check++;
7672     }
7673   }
7674
7675   if (check != num_changed_custom_elements)     // should not happen
7676     Warn("inconsistent number of custom element properties");
7677 }
7678 #endif
7679
7680 #if ENABLE_HISTORIC_CHUNKS
7681 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7682                            int num_changed_custom_elements)
7683 {
7684   int i, check = 0;
7685
7686   putFile16BitBE(file, num_changed_custom_elements);
7687
7688   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7689   {
7690     int element = EL_CUSTOM_START + i;
7691
7692     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7693     {
7694       if (check < num_changed_custom_elements)
7695       {
7696         putFile16BitBE(file, element);
7697         putFile16BitBE(file, element_info[element].change->target_element);
7698       }
7699
7700       check++;
7701     }
7702   }
7703
7704   if (check != num_changed_custom_elements)     // should not happen
7705     Warn("inconsistent number of custom target elements");
7706 }
7707 #endif
7708
7709 #if ENABLE_HISTORIC_CHUNKS
7710 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7711                            int num_changed_custom_elements)
7712 {
7713   int i, j, x, y, check = 0;
7714
7715   putFile16BitBE(file, num_changed_custom_elements);
7716
7717   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7718   {
7719     int element = EL_CUSTOM_START + i;
7720     struct ElementInfo *ei = &element_info[element];
7721
7722     if (ei->modified_settings)
7723     {
7724       if (check < num_changed_custom_elements)
7725       {
7726         putFile16BitBE(file, element);
7727
7728         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7729           putFile8Bit(file, ei->description[j]);
7730
7731         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7732
7733         // some free bytes for future properties and padding
7734         WriteUnusedBytesToFile(file, 7);
7735
7736         putFile8Bit(file, ei->use_gfx_element);
7737         putFile16BitBE(file, ei->gfx_element_initial);
7738
7739         putFile8Bit(file, ei->collect_score_initial);
7740         putFile8Bit(file, ei->collect_count_initial);
7741
7742         putFile16BitBE(file, ei->push_delay_fixed);
7743         putFile16BitBE(file, ei->push_delay_random);
7744         putFile16BitBE(file, ei->move_delay_fixed);
7745         putFile16BitBE(file, ei->move_delay_random);
7746
7747         putFile16BitBE(file, ei->move_pattern);
7748         putFile8Bit(file, ei->move_direction_initial);
7749         putFile8Bit(file, ei->move_stepsize);
7750
7751         for (y = 0; y < 3; y++)
7752           for (x = 0; x < 3; x++)
7753             putFile16BitBE(file, ei->content.e[x][y]);
7754
7755         putFile32BitBE(file, ei->change->events);
7756
7757         putFile16BitBE(file, ei->change->target_element);
7758
7759         putFile16BitBE(file, ei->change->delay_fixed);
7760         putFile16BitBE(file, ei->change->delay_random);
7761         putFile16BitBE(file, ei->change->delay_frames);
7762
7763         putFile16BitBE(file, ei->change->initial_trigger_element);
7764
7765         putFile8Bit(file, ei->change->explode);
7766         putFile8Bit(file, ei->change->use_target_content);
7767         putFile8Bit(file, ei->change->only_if_complete);
7768         putFile8Bit(file, ei->change->use_random_replace);
7769
7770         putFile8Bit(file, ei->change->random_percentage);
7771         putFile8Bit(file, ei->change->replace_when);
7772
7773         for (y = 0; y < 3; y++)
7774           for (x = 0; x < 3; x++)
7775             putFile16BitBE(file, ei->change->content.e[x][y]);
7776
7777         putFile8Bit(file, ei->slippery_type);
7778
7779         // some free bytes for future properties and padding
7780         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7781       }
7782
7783       check++;
7784     }
7785   }
7786
7787   if (check != num_changed_custom_elements)     // should not happen
7788     Warn("inconsistent number of custom element properties");
7789 }
7790 #endif
7791
7792 #if ENABLE_HISTORIC_CHUNKS
7793 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7794 {
7795   struct ElementInfo *ei = &element_info[element];
7796   int i, j, x, y;
7797
7798   // ---------- custom element base property values (96 bytes) ----------------
7799
7800   putFile16BitBE(file, element);
7801
7802   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7803     putFile8Bit(file, ei->description[i]);
7804
7805   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7806
7807   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
7808
7809   putFile8Bit(file, ei->num_change_pages);
7810
7811   putFile16BitBE(file, ei->ce_value_fixed_initial);
7812   putFile16BitBE(file, ei->ce_value_random_initial);
7813   putFile8Bit(file, ei->use_last_ce_value);
7814
7815   putFile8Bit(file, ei->use_gfx_element);
7816   putFile16BitBE(file, ei->gfx_element_initial);
7817
7818   putFile8Bit(file, ei->collect_score_initial);
7819   putFile8Bit(file, ei->collect_count_initial);
7820
7821   putFile8Bit(file, ei->drop_delay_fixed);
7822   putFile8Bit(file, ei->push_delay_fixed);
7823   putFile8Bit(file, ei->drop_delay_random);
7824   putFile8Bit(file, ei->push_delay_random);
7825   putFile16BitBE(file, ei->move_delay_fixed);
7826   putFile16BitBE(file, ei->move_delay_random);
7827
7828   // bits 0 - 15 of "move_pattern" ...
7829   putFile16BitBE(file, ei->move_pattern & 0xffff);
7830   putFile8Bit(file, ei->move_direction_initial);
7831   putFile8Bit(file, ei->move_stepsize);
7832
7833   putFile8Bit(file, ei->slippery_type);
7834
7835   for (y = 0; y < 3; y++)
7836     for (x = 0; x < 3; x++)
7837       putFile16BitBE(file, ei->content.e[x][y]);
7838
7839   putFile16BitBE(file, ei->move_enter_element);
7840   putFile16BitBE(file, ei->move_leave_element);
7841   putFile8Bit(file, ei->move_leave_type);
7842
7843   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7844   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7845
7846   putFile8Bit(file, ei->access_direction);
7847
7848   putFile8Bit(file, ei->explosion_delay);
7849   putFile8Bit(file, ei->ignition_delay);
7850   putFile8Bit(file, ei->explosion_type);
7851
7852   // some free bytes for future custom property values and padding
7853   WriteUnusedBytesToFile(file, 1);
7854
7855   // ---------- change page property values (48 bytes) ------------------------
7856
7857   for (i = 0; i < ei->num_change_pages; i++)
7858   {
7859     struct ElementChangeInfo *change = &ei->change_page[i];
7860     unsigned int event_bits;
7861
7862     // bits 0 - 31 of "has_event[]" ...
7863     event_bits = 0;
7864     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7865       if (change->has_event[j])
7866         event_bits |= (1u << j);
7867     putFile32BitBE(file, event_bits);
7868
7869     putFile16BitBE(file, change->target_element);
7870
7871     putFile16BitBE(file, change->delay_fixed);
7872     putFile16BitBE(file, change->delay_random);
7873     putFile16BitBE(file, change->delay_frames);
7874
7875     putFile16BitBE(file, change->initial_trigger_element);
7876
7877     putFile8Bit(file, change->explode);
7878     putFile8Bit(file, change->use_target_content);
7879     putFile8Bit(file, change->only_if_complete);
7880     putFile8Bit(file, change->use_random_replace);
7881
7882     putFile8Bit(file, change->random_percentage);
7883     putFile8Bit(file, change->replace_when);
7884
7885     for (y = 0; y < 3; y++)
7886       for (x = 0; x < 3; x++)
7887         putFile16BitBE(file, change->target_content.e[x][y]);
7888
7889     putFile8Bit(file, change->can_change);
7890
7891     putFile8Bit(file, change->trigger_side);
7892
7893     putFile8Bit(file, change->trigger_player);
7894     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7895                        log_2(change->trigger_page)));
7896
7897     putFile8Bit(file, change->has_action);
7898     putFile8Bit(file, change->action_type);
7899     putFile8Bit(file, change->action_mode);
7900     putFile16BitBE(file, change->action_arg);
7901
7902     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7903     event_bits = 0;
7904     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7905       if (change->has_event[j])
7906         event_bits |= (1u << (j - 32));
7907     putFile8Bit(file, event_bits);
7908   }
7909 }
7910 #endif
7911
7912 #if ENABLE_HISTORIC_CHUNKS
7913 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7914 {
7915   struct ElementInfo *ei = &element_info[element];
7916   struct ElementGroupInfo *group = ei->group;
7917   int i;
7918
7919   putFile16BitBE(file, element);
7920
7921   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7922     putFile8Bit(file, ei->description[i]);
7923
7924   putFile8Bit(file, group->num_elements);
7925
7926   putFile8Bit(file, ei->use_gfx_element);
7927   putFile16BitBE(file, ei->gfx_element_initial);
7928
7929   putFile8Bit(file, group->choice_mode);
7930
7931   // some free bytes for future values and padding
7932   WriteUnusedBytesToFile(file, 3);
7933
7934   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7935     putFile16BitBE(file, group->element[i]);
7936 }
7937 #endif
7938
7939 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7940                                 boolean write_element)
7941 {
7942   int save_type = entry->save_type;
7943   int data_type = entry->data_type;
7944   int conf_type = entry->conf_type;
7945   int byte_mask = conf_type & CONF_MASK_BYTES;
7946   int element = entry->element;
7947   int default_value = entry->default_value;
7948   int num_bytes = 0;
7949   boolean modified = FALSE;
7950
7951   if (byte_mask != CONF_MASK_MULTI_BYTES)
7952   {
7953     void *value_ptr = entry->value;
7954     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7955                  *(int *)value_ptr);
7956
7957     // check if any settings have been modified before saving them
7958     if (value != default_value)
7959       modified = TRUE;
7960
7961     // do not save if explicitly told or if unmodified default settings
7962     if ((save_type == SAVE_CONF_NEVER) ||
7963         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7964       return 0;
7965
7966     if (write_element)
7967       num_bytes += putFile16BitBE(file, element);
7968
7969     num_bytes += putFile8Bit(file, conf_type);
7970     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7971                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7972                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7973                   0);
7974   }
7975   else if (data_type == TYPE_STRING)
7976   {
7977     char *default_string = entry->default_string;
7978     char *string = (char *)(entry->value);
7979     int string_length = strlen(string);
7980     int i;
7981
7982     // check if any settings have been modified before saving them
7983     if (!strEqual(string, default_string))
7984       modified = TRUE;
7985
7986     // do not save if explicitly told or if unmodified default settings
7987     if ((save_type == SAVE_CONF_NEVER) ||
7988         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7989       return 0;
7990
7991     if (write_element)
7992       num_bytes += putFile16BitBE(file, element);
7993
7994     num_bytes += putFile8Bit(file, conf_type);
7995     num_bytes += putFile16BitBE(file, string_length);
7996
7997     for (i = 0; i < string_length; i++)
7998       num_bytes += putFile8Bit(file, string[i]);
7999   }
8000   else if (data_type == TYPE_ELEMENT_LIST)
8001   {
8002     int *element_array = (int *)(entry->value);
8003     int num_elements = *(int *)(entry->num_entities);
8004     int i;
8005
8006     // check if any settings have been modified before saving them
8007     for (i = 0; i < num_elements; i++)
8008       if (element_array[i] != default_value)
8009         modified = TRUE;
8010
8011     // do not save if explicitly told or if unmodified default settings
8012     if ((save_type == SAVE_CONF_NEVER) ||
8013         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8014       return 0;
8015
8016     if (write_element)
8017       num_bytes += putFile16BitBE(file, element);
8018
8019     num_bytes += putFile8Bit(file, conf_type);
8020     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8021
8022     for (i = 0; i < num_elements; i++)
8023       num_bytes += putFile16BitBE(file, element_array[i]);
8024   }
8025   else if (data_type == TYPE_CONTENT_LIST)
8026   {
8027     struct Content *content = (struct Content *)(entry->value);
8028     int num_contents = *(int *)(entry->num_entities);
8029     int i, x, y;
8030
8031     // check if any settings have been modified before saving them
8032     for (i = 0; i < num_contents; i++)
8033       for (y = 0; y < 3; y++)
8034         for (x = 0; x < 3; x++)
8035           if (content[i].e[x][y] != default_value)
8036             modified = TRUE;
8037
8038     // do not save if explicitly told or if unmodified default settings
8039     if ((save_type == SAVE_CONF_NEVER) ||
8040         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8041       return 0;
8042
8043     if (write_element)
8044       num_bytes += putFile16BitBE(file, element);
8045
8046     num_bytes += putFile8Bit(file, conf_type);
8047     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8048
8049     for (i = 0; i < num_contents; i++)
8050       for (y = 0; y < 3; y++)
8051         for (x = 0; x < 3; x++)
8052           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8053   }
8054
8055   return num_bytes;
8056 }
8057
8058 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8059 {
8060   int chunk_size = 0;
8061   int i;
8062
8063   li = *level;          // copy level data into temporary buffer
8064
8065   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8066     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8067
8068   return chunk_size;
8069 }
8070
8071 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8072 {
8073   int chunk_size = 0;
8074   int i;
8075
8076   li = *level;          // copy level data into temporary buffer
8077
8078   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8079     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8080
8081   return chunk_size;
8082 }
8083
8084 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8085 {
8086   int envelope_nr = element - EL_ENVELOPE_1;
8087   int chunk_size = 0;
8088   int i;
8089
8090   chunk_size += putFile16BitBE(file, element);
8091
8092   // copy envelope data into temporary buffer
8093   xx_envelope = level->envelope[envelope_nr];
8094
8095   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8096     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8097
8098   return chunk_size;
8099 }
8100
8101 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8102 {
8103   struct ElementInfo *ei = &element_info[element];
8104   int chunk_size = 0;
8105   int i, j;
8106
8107   chunk_size += putFile16BitBE(file, element);
8108
8109   xx_ei = *ei;          // copy element data into temporary buffer
8110
8111   // set default description string for this specific element
8112   strcpy(xx_default_description, getDefaultElementDescription(ei));
8113
8114   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8115     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8116
8117   for (i = 0; i < ei->num_change_pages; i++)
8118   {
8119     struct ElementChangeInfo *change = &ei->change_page[i];
8120
8121     xx_current_change_page = i;
8122
8123     xx_change = *change;        // copy change data into temporary buffer
8124
8125     resetEventBits();
8126     setEventBitsFromEventFlags(change);
8127
8128     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8129       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8130                                          FALSE);
8131   }
8132
8133   return chunk_size;
8134 }
8135
8136 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8137 {
8138   struct ElementInfo *ei = &element_info[element];
8139   struct ElementGroupInfo *group = ei->group;
8140   int chunk_size = 0;
8141   int i;
8142
8143   chunk_size += putFile16BitBE(file, element);
8144
8145   xx_ei = *ei;          // copy element data into temporary buffer
8146   xx_group = *group;    // copy group data into temporary buffer
8147
8148   // set default description string for this specific element
8149   strcpy(xx_default_description, getDefaultElementDescription(ei));
8150
8151   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8152     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8153
8154   return chunk_size;
8155 }
8156
8157 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8158 {
8159   struct ElementInfo *ei = &element_info[element];
8160   int chunk_size = 0;
8161   int i;
8162
8163   chunk_size += putFile16BitBE(file, element);
8164
8165   xx_ei = *ei;          // copy element data into temporary buffer
8166
8167   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8168     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8169
8170   return chunk_size;
8171 }
8172
8173 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8174                                   boolean save_as_template)
8175 {
8176   int chunk_size;
8177   int i;
8178   FILE *file;
8179
8180   if (!(file = fopen(filename, MODE_WRITE)))
8181   {
8182     Warn("cannot save level file '%s'", filename);
8183
8184     return;
8185   }
8186
8187   level->file_version = FILE_VERSION_ACTUAL;
8188   level->game_version = GAME_VERSION_ACTUAL;
8189
8190   level->creation_date = getCurrentDate();
8191
8192   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8193   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8194
8195   chunk_size = SaveLevel_VERS(NULL, level);
8196   putFileChunkBE(file, "VERS", chunk_size);
8197   SaveLevel_VERS(file, level);
8198
8199   chunk_size = SaveLevel_DATE(NULL, level);
8200   putFileChunkBE(file, "DATE", chunk_size);
8201   SaveLevel_DATE(file, level);
8202
8203   chunk_size = SaveLevel_NAME(NULL, level);
8204   putFileChunkBE(file, "NAME", chunk_size);
8205   SaveLevel_NAME(file, level);
8206
8207   chunk_size = SaveLevel_AUTH(NULL, level);
8208   putFileChunkBE(file, "AUTH", chunk_size);
8209   SaveLevel_AUTH(file, level);
8210
8211   chunk_size = SaveLevel_INFO(NULL, level);
8212   putFileChunkBE(file, "INFO", chunk_size);
8213   SaveLevel_INFO(file, level);
8214
8215   chunk_size = SaveLevel_BODY(NULL, level);
8216   putFileChunkBE(file, "BODY", chunk_size);
8217   SaveLevel_BODY(file, level);
8218
8219   chunk_size = SaveLevel_ELEM(NULL, level);
8220   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8221   {
8222     putFileChunkBE(file, "ELEM", chunk_size);
8223     SaveLevel_ELEM(file, level);
8224   }
8225
8226   for (i = 0; i < NUM_ENVELOPES; i++)
8227   {
8228     int element = EL_ENVELOPE_1 + i;
8229
8230     chunk_size = SaveLevel_NOTE(NULL, level, element);
8231     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8232     {
8233       putFileChunkBE(file, "NOTE", chunk_size);
8234       SaveLevel_NOTE(file, level, element);
8235     }
8236   }
8237
8238   // if not using template level, check for non-default custom/group elements
8239   if (!level->use_custom_template || save_as_template)
8240   {
8241     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8242     {
8243       int element = EL_CUSTOM_START + i;
8244
8245       chunk_size = SaveLevel_CUSX(NULL, level, element);
8246       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8247       {
8248         putFileChunkBE(file, "CUSX", chunk_size);
8249         SaveLevel_CUSX(file, level, element);
8250       }
8251     }
8252
8253     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8254     {
8255       int element = EL_GROUP_START + i;
8256
8257       chunk_size = SaveLevel_GRPX(NULL, level, element);
8258       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8259       {
8260         putFileChunkBE(file, "GRPX", chunk_size);
8261         SaveLevel_GRPX(file, level, element);
8262       }
8263     }
8264
8265     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8266     {
8267       int element = GET_EMPTY_ELEMENT(i);
8268
8269       chunk_size = SaveLevel_EMPX(NULL, level, element);
8270       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8271       {
8272         putFileChunkBE(file, "EMPX", chunk_size);
8273         SaveLevel_EMPX(file, level, element);
8274       }
8275     }
8276   }
8277
8278   fclose(file);
8279
8280   SetFilePermissions(filename, PERMS_PRIVATE);
8281 }
8282
8283 void SaveLevel(int nr)
8284 {
8285   char *filename = getDefaultLevelFilename(nr);
8286
8287   SaveLevelFromFilename(&level, filename, FALSE);
8288 }
8289
8290 void SaveLevelTemplate(void)
8291 {
8292   char *filename = getLocalLevelTemplateFilename();
8293
8294   SaveLevelFromFilename(&level, filename, TRUE);
8295 }
8296
8297 boolean SaveLevelChecked(int nr)
8298 {
8299   char *filename = getDefaultLevelFilename(nr);
8300   boolean new_level = !fileExists(filename);
8301   boolean level_saved = FALSE;
8302
8303   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8304   {
8305     SaveLevel(nr);
8306
8307     if (new_level)
8308       Request("Level saved!", REQ_CONFIRM);
8309
8310     level_saved = TRUE;
8311   }
8312
8313   return level_saved;
8314 }
8315
8316 void DumpLevel(struct LevelInfo *level)
8317 {
8318   if (level->no_level_file || level->no_valid_file)
8319   {
8320     Warn("cannot dump -- no valid level file found");
8321
8322     return;
8323   }
8324
8325   PrintLine("-", 79);
8326   Print("Level xxx (file version %08d, game version %08d)\n",
8327         level->file_version, level->game_version);
8328   PrintLine("-", 79);
8329
8330   Print("Level author: '%s'\n", level->author);
8331   Print("Level title:  '%s'\n", level->name);
8332   Print("\n");
8333   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8334   Print("\n");
8335   Print("Level time:  %d seconds\n", level->time);
8336   Print("Gems needed: %d\n", level->gems_needed);
8337   Print("\n");
8338   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8339   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8340   Print("Time for light:      %d seconds\n", level->time_light);
8341   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8342   Print("\n");
8343   Print("Amoeba speed: %d\n", level->amoeba_speed);
8344   Print("\n");
8345
8346   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8347   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8348   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8349   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8350   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8351   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8352
8353   if (options.debug)
8354   {
8355     int i, j;
8356
8357     for (i = 0; i < NUM_ENVELOPES; i++)
8358     {
8359       char *text = level->envelope[i].text;
8360       int text_len = strlen(text);
8361       boolean has_text = FALSE;
8362
8363       for (j = 0; j < text_len; j++)
8364         if (text[j] != ' ' && text[j] != '\n')
8365           has_text = TRUE;
8366
8367       if (has_text)
8368       {
8369         Print("\n");
8370         Print("Envelope %d:\n'%s'\n", i + 1, text);
8371       }
8372     }
8373   }
8374
8375   PrintLine("-", 79);
8376 }
8377
8378 void DumpLevels(void)
8379 {
8380   static LevelDirTree *dumplevel_leveldir = NULL;
8381
8382   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8383                                                  global.dumplevel_leveldir);
8384
8385   if (dumplevel_leveldir == NULL)
8386     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8387
8388   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8389       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8390     Fail("no such level number: %d", global.dumplevel_level_nr);
8391
8392   leveldir_current = dumplevel_leveldir;
8393
8394   LoadLevel(global.dumplevel_level_nr);
8395   DumpLevel(&level);
8396
8397   CloseAllAndExit(0);
8398 }
8399
8400
8401 // ============================================================================
8402 // tape file functions
8403 // ============================================================================
8404
8405 static void setTapeInfoToDefaults(void)
8406 {
8407   int i;
8408
8409   // always start with reliable default values (empty tape)
8410   TapeErase();
8411
8412   // default values (also for pre-1.2 tapes) with only the first player
8413   tape.player_participates[0] = TRUE;
8414   for (i = 1; i < MAX_PLAYERS; i++)
8415     tape.player_participates[i] = FALSE;
8416
8417   // at least one (default: the first) player participates in every tape
8418   tape.num_participating_players = 1;
8419
8420   tape.property_bits = TAPE_PROPERTY_NONE;
8421
8422   tape.level_nr = level_nr;
8423   tape.counter = 0;
8424   tape.changed = FALSE;
8425   tape.solved = FALSE;
8426
8427   tape.recording = FALSE;
8428   tape.playing = FALSE;
8429   tape.pausing = FALSE;
8430
8431   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8432   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8433
8434   tape.no_info_chunk = TRUE;
8435   tape.no_valid_file = FALSE;
8436 }
8437
8438 static int getTapePosSize(struct TapeInfo *tape)
8439 {
8440   int tape_pos_size = 0;
8441
8442   if (tape->use_key_actions)
8443     tape_pos_size += tape->num_participating_players;
8444
8445   if (tape->use_mouse_actions)
8446     tape_pos_size += 3;         // x and y position and mouse button mask
8447
8448   tape_pos_size += 1;           // tape action delay value
8449
8450   return tape_pos_size;
8451 }
8452
8453 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8454 {
8455   tape->use_key_actions = FALSE;
8456   tape->use_mouse_actions = FALSE;
8457
8458   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8459     tape->use_key_actions = TRUE;
8460
8461   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8462     tape->use_mouse_actions = TRUE;
8463 }
8464
8465 static int getTapeActionValue(struct TapeInfo *tape)
8466 {
8467   return (tape->use_key_actions &&
8468           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8469           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8470           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8471           TAPE_ACTIONS_DEFAULT);
8472 }
8473
8474 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8475 {
8476   tape->file_version = getFileVersion(file);
8477   tape->game_version = getFileVersion(file);
8478
8479   return chunk_size;
8480 }
8481
8482 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8483 {
8484   int i;
8485
8486   tape->random_seed = getFile32BitBE(file);
8487   tape->date        = getFile32BitBE(file);
8488   tape->length      = getFile32BitBE(file);
8489
8490   // read header fields that are new since version 1.2
8491   if (tape->file_version >= FILE_VERSION_1_2)
8492   {
8493     byte store_participating_players = getFile8Bit(file);
8494     int engine_version;
8495
8496     // since version 1.2, tapes store which players participate in the tape
8497     tape->num_participating_players = 0;
8498     for (i = 0; i < MAX_PLAYERS; i++)
8499     {
8500       tape->player_participates[i] = FALSE;
8501
8502       if (store_participating_players & (1 << i))
8503       {
8504         tape->player_participates[i] = TRUE;
8505         tape->num_participating_players++;
8506       }
8507     }
8508
8509     setTapeActionFlags(tape, getFile8Bit(file));
8510
8511     tape->property_bits = getFile8Bit(file);
8512     tape->solved = getFile8Bit(file);
8513
8514     engine_version = getFileVersion(file);
8515     if (engine_version > 0)
8516       tape->engine_version = engine_version;
8517     else
8518       tape->engine_version = tape->game_version;
8519   }
8520
8521   return chunk_size;
8522 }
8523
8524 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8525 {
8526   tape->scr_fieldx = getFile8Bit(file);
8527   tape->scr_fieldy = getFile8Bit(file);
8528
8529   return chunk_size;
8530 }
8531
8532 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8533 {
8534   char *level_identifier = NULL;
8535   int level_identifier_size;
8536   int i;
8537
8538   tape->no_info_chunk = FALSE;
8539
8540   level_identifier_size = getFile16BitBE(file);
8541
8542   level_identifier = checked_malloc(level_identifier_size);
8543
8544   for (i = 0; i < level_identifier_size; i++)
8545     level_identifier[i] = getFile8Bit(file);
8546
8547   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8548   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8549
8550   checked_free(level_identifier);
8551
8552   tape->level_nr = getFile16BitBE(file);
8553
8554   chunk_size = 2 + level_identifier_size + 2;
8555
8556   return chunk_size;
8557 }
8558
8559 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8560 {
8561   int i, j;
8562   int tape_pos_size = getTapePosSize(tape);
8563   int chunk_size_expected = tape_pos_size * tape->length;
8564
8565   if (chunk_size_expected != chunk_size)
8566   {
8567     ReadUnusedBytesFromFile(file, chunk_size);
8568     return chunk_size_expected;
8569   }
8570
8571   for (i = 0; i < tape->length; i++)
8572   {
8573     if (i >= MAX_TAPE_LEN)
8574     {
8575       Warn("tape truncated -- size exceeds maximum tape size %d",
8576             MAX_TAPE_LEN);
8577
8578       // tape too large; read and ignore remaining tape data from this chunk
8579       for (;i < tape->length; i++)
8580         ReadUnusedBytesFromFile(file, tape_pos_size);
8581
8582       break;
8583     }
8584
8585     if (tape->use_key_actions)
8586     {
8587       for (j = 0; j < MAX_PLAYERS; j++)
8588       {
8589         tape->pos[i].action[j] = MV_NONE;
8590
8591         if (tape->player_participates[j])
8592           tape->pos[i].action[j] = getFile8Bit(file);
8593       }
8594     }
8595
8596     if (tape->use_mouse_actions)
8597     {
8598       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8599       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8600       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8601     }
8602
8603     tape->pos[i].delay = getFile8Bit(file);
8604
8605     if (tape->file_version == FILE_VERSION_1_0)
8606     {
8607       // eliminate possible diagonal moves in old tapes
8608       // this is only for backward compatibility
8609
8610       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8611       byte action = tape->pos[i].action[0];
8612       int k, num_moves = 0;
8613
8614       for (k = 0; k < 4; k++)
8615       {
8616         if (action & joy_dir[k])
8617         {
8618           tape->pos[i + num_moves].action[0] = joy_dir[k];
8619           if (num_moves > 0)
8620             tape->pos[i + num_moves].delay = 0;
8621           num_moves++;
8622         }
8623       }
8624
8625       if (num_moves > 1)
8626       {
8627         num_moves--;
8628         i += num_moves;
8629         tape->length += num_moves;
8630       }
8631     }
8632     else if (tape->file_version < FILE_VERSION_2_0)
8633     {
8634       // convert pre-2.0 tapes to new tape format
8635
8636       if (tape->pos[i].delay > 1)
8637       {
8638         // action part
8639         tape->pos[i + 1] = tape->pos[i];
8640         tape->pos[i + 1].delay = 1;
8641
8642         // delay part
8643         for (j = 0; j < MAX_PLAYERS; j++)
8644           tape->pos[i].action[j] = MV_NONE;
8645         tape->pos[i].delay--;
8646
8647         i++;
8648         tape->length++;
8649       }
8650     }
8651
8652     if (checkEndOfFile(file))
8653       break;
8654   }
8655
8656   if (i != tape->length)
8657     chunk_size = tape_pos_size * i;
8658
8659   return chunk_size;
8660 }
8661
8662 static void LoadTape_SokobanSolution(char *filename)
8663 {
8664   File *file;
8665   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8666
8667   if (!(file = openFile(filename, MODE_READ)))
8668   {
8669     tape.no_valid_file = TRUE;
8670
8671     return;
8672   }
8673
8674   while (!checkEndOfFile(file))
8675   {
8676     unsigned char c = getByteFromFile(file);
8677
8678     if (checkEndOfFile(file))
8679       break;
8680
8681     switch (c)
8682     {
8683       case 'u':
8684       case 'U':
8685         tape.pos[tape.length].action[0] = MV_UP;
8686         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8687         tape.length++;
8688         break;
8689
8690       case 'd':
8691       case 'D':
8692         tape.pos[tape.length].action[0] = MV_DOWN;
8693         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8694         tape.length++;
8695         break;
8696
8697       case 'l':
8698       case 'L':
8699         tape.pos[tape.length].action[0] = MV_LEFT;
8700         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8701         tape.length++;
8702         break;
8703
8704       case 'r':
8705       case 'R':
8706         tape.pos[tape.length].action[0] = MV_RIGHT;
8707         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8708         tape.length++;
8709         break;
8710
8711       case '\n':
8712       case '\r':
8713       case '\t':
8714       case ' ':
8715         // ignore white-space characters
8716         break;
8717
8718       default:
8719         tape.no_valid_file = TRUE;
8720
8721         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8722
8723         break;
8724     }
8725   }
8726
8727   closeFile(file);
8728
8729   if (tape.no_valid_file)
8730     return;
8731
8732   tape.length_frames  = GetTapeLengthFrames();
8733   tape.length_seconds = GetTapeLengthSeconds();
8734 }
8735
8736 void LoadTapeFromFilename(char *filename)
8737 {
8738   char cookie[MAX_LINE_LEN];
8739   char chunk_name[CHUNK_ID_LEN + 1];
8740   File *file;
8741   int chunk_size;
8742
8743   // always start with reliable default values
8744   setTapeInfoToDefaults();
8745
8746   if (strSuffix(filename, ".sln"))
8747   {
8748     LoadTape_SokobanSolution(filename);
8749
8750     return;
8751   }
8752
8753   if (!(file = openFile(filename, MODE_READ)))
8754   {
8755     tape.no_valid_file = TRUE;
8756
8757     return;
8758   }
8759
8760   getFileChunkBE(file, chunk_name, NULL);
8761   if (strEqual(chunk_name, "RND1"))
8762   {
8763     getFile32BitBE(file);               // not used
8764
8765     getFileChunkBE(file, chunk_name, NULL);
8766     if (!strEqual(chunk_name, "TAPE"))
8767     {
8768       tape.no_valid_file = TRUE;
8769
8770       Warn("unknown format of tape file '%s'", filename);
8771
8772       closeFile(file);
8773
8774       return;
8775     }
8776   }
8777   else  // check for pre-2.0 file format with cookie string
8778   {
8779     strcpy(cookie, chunk_name);
8780     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8781       cookie[4] = '\0';
8782     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8783       cookie[strlen(cookie) - 1] = '\0';
8784
8785     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8786     {
8787       tape.no_valid_file = TRUE;
8788
8789       Warn("unknown format of tape file '%s'", filename);
8790
8791       closeFile(file);
8792
8793       return;
8794     }
8795
8796     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8797     {
8798       tape.no_valid_file = TRUE;
8799
8800       Warn("unsupported version of tape file '%s'", filename);
8801
8802       closeFile(file);
8803
8804       return;
8805     }
8806
8807     // pre-2.0 tape files have no game version, so use file version here
8808     tape.game_version = tape.file_version;
8809   }
8810
8811   if (tape.file_version < FILE_VERSION_1_2)
8812   {
8813     // tape files from versions before 1.2.0 without chunk structure
8814     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8815     LoadTape_BODY(file, 2 * tape.length,      &tape);
8816   }
8817   else
8818   {
8819     static struct
8820     {
8821       char *name;
8822       int size;
8823       int (*loader)(File *, int, struct TapeInfo *);
8824     }
8825     chunk_info[] =
8826     {
8827       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
8828       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
8829       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
8830       { "INFO", -1,                     LoadTape_INFO },
8831       { "BODY", -1,                     LoadTape_BODY },
8832       {  NULL,  0,                      NULL }
8833     };
8834
8835     while (getFileChunkBE(file, chunk_name, &chunk_size))
8836     {
8837       int i = 0;
8838
8839       while (chunk_info[i].name != NULL &&
8840              !strEqual(chunk_name, chunk_info[i].name))
8841         i++;
8842
8843       if (chunk_info[i].name == NULL)
8844       {
8845         Warn("unknown chunk '%s' in tape file '%s'",
8846               chunk_name, filename);
8847
8848         ReadUnusedBytesFromFile(file, chunk_size);
8849       }
8850       else if (chunk_info[i].size != -1 &&
8851                chunk_info[i].size != chunk_size)
8852       {
8853         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8854               chunk_size, chunk_name, filename);
8855
8856         ReadUnusedBytesFromFile(file, chunk_size);
8857       }
8858       else
8859       {
8860         // call function to load this tape chunk
8861         int chunk_size_expected =
8862           (chunk_info[i].loader)(file, chunk_size, &tape);
8863
8864         // the size of some chunks cannot be checked before reading other
8865         // chunks first (like "HEAD" and "BODY") that contain some header
8866         // information, so check them here
8867         if (chunk_size_expected != chunk_size)
8868         {
8869           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8870                 chunk_size, chunk_name, filename);
8871         }
8872       }
8873     }
8874   }
8875
8876   closeFile(file);
8877
8878   tape.length_frames  = GetTapeLengthFrames();
8879   tape.length_seconds = GetTapeLengthSeconds();
8880
8881 #if 0
8882   Debug("files:LoadTapeFromFilename", "tape file version: %d",
8883         tape.file_version);
8884   Debug("files:LoadTapeFromFilename", "tape game version: %d",
8885         tape.game_version);
8886   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8887         tape.engine_version);
8888 #endif
8889 }
8890
8891 void LoadTape(int nr)
8892 {
8893   char *filename = getTapeFilename(nr);
8894
8895   LoadTapeFromFilename(filename);
8896 }
8897
8898 void LoadSolutionTape(int nr)
8899 {
8900   char *filename = getSolutionTapeFilename(nr);
8901
8902   LoadTapeFromFilename(filename);
8903
8904   if (TAPE_IS_EMPTY(tape))
8905   {
8906     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8907         level.native_bd_level->replay != NULL)
8908       CopyNativeTape_BD_to_RND(&level);
8909     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8910         level.native_sp_level->demo.is_available)
8911       CopyNativeTape_SP_to_RND(&level);
8912   }
8913 }
8914
8915 void LoadScoreTape(char *score_tape_basename, int nr)
8916 {
8917   char *filename = getScoreTapeFilename(score_tape_basename, nr);
8918
8919   LoadTapeFromFilename(filename);
8920 }
8921
8922 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8923 {
8924   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8925
8926   LoadTapeFromFilename(filename);
8927 }
8928
8929 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8930 {
8931   // chunk required for team mode tapes with non-default screen size
8932   return (tape->num_participating_players > 1 &&
8933           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8934            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8935 }
8936
8937 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8938 {
8939   putFileVersion(file, tape->file_version);
8940   putFileVersion(file, tape->game_version);
8941 }
8942
8943 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8944 {
8945   int i;
8946   byte store_participating_players = 0;
8947
8948   // set bits for participating players for compact storage
8949   for (i = 0; i < MAX_PLAYERS; i++)
8950     if (tape->player_participates[i])
8951       store_participating_players |= (1 << i);
8952
8953   putFile32BitBE(file, tape->random_seed);
8954   putFile32BitBE(file, tape->date);
8955   putFile32BitBE(file, tape->length);
8956
8957   putFile8Bit(file, store_participating_players);
8958
8959   putFile8Bit(file, getTapeActionValue(tape));
8960
8961   putFile8Bit(file, tape->property_bits);
8962   putFile8Bit(file, tape->solved);
8963
8964   putFileVersion(file, tape->engine_version);
8965 }
8966
8967 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8968 {
8969   putFile8Bit(file, tape->scr_fieldx);
8970   putFile8Bit(file, tape->scr_fieldy);
8971 }
8972
8973 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8974 {
8975   int level_identifier_size = strlen(tape->level_identifier) + 1;
8976   int i;
8977
8978   putFile16BitBE(file, level_identifier_size);
8979
8980   for (i = 0; i < level_identifier_size; i++)
8981     putFile8Bit(file, tape->level_identifier[i]);
8982
8983   putFile16BitBE(file, tape->level_nr);
8984 }
8985
8986 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8987 {
8988   int i, j;
8989
8990   for (i = 0; i < tape->length; i++)
8991   {
8992     if (tape->use_key_actions)
8993     {
8994       for (j = 0; j < MAX_PLAYERS; j++)
8995         if (tape->player_participates[j])
8996           putFile8Bit(file, tape->pos[i].action[j]);
8997     }
8998
8999     if (tape->use_mouse_actions)
9000     {
9001       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9002       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9003       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9004     }
9005
9006     putFile8Bit(file, tape->pos[i].delay);
9007   }
9008 }
9009
9010 void SaveTapeToFilename(char *filename)
9011 {
9012   FILE *file;
9013   int tape_pos_size;
9014   int info_chunk_size;
9015   int body_chunk_size;
9016
9017   if (!(file = fopen(filename, MODE_WRITE)))
9018   {
9019     Warn("cannot save level recording file '%s'", filename);
9020
9021     return;
9022   }
9023
9024   tape_pos_size = getTapePosSize(&tape);
9025
9026   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9027   body_chunk_size = tape_pos_size * tape.length;
9028
9029   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9030   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9031
9032   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9033   SaveTape_VERS(file, &tape);
9034
9035   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9036   SaveTape_HEAD(file, &tape);
9037
9038   if (checkSaveTape_SCRN(&tape))
9039   {
9040     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9041     SaveTape_SCRN(file, &tape);
9042   }
9043
9044   putFileChunkBE(file, "INFO", info_chunk_size);
9045   SaveTape_INFO(file, &tape);
9046
9047   putFileChunkBE(file, "BODY", body_chunk_size);
9048   SaveTape_BODY(file, &tape);
9049
9050   fclose(file);
9051
9052   SetFilePermissions(filename, PERMS_PRIVATE);
9053 }
9054
9055 static void SaveTapeExt(char *filename)
9056 {
9057   int i;
9058
9059   tape.file_version = FILE_VERSION_ACTUAL;
9060   tape.game_version = GAME_VERSION_ACTUAL;
9061
9062   tape.num_participating_players = 0;
9063
9064   // count number of participating players
9065   for (i = 0; i < MAX_PLAYERS; i++)
9066     if (tape.player_participates[i])
9067       tape.num_participating_players++;
9068
9069   SaveTapeToFilename(filename);
9070
9071   tape.changed = FALSE;
9072 }
9073
9074 void SaveTape(int nr)
9075 {
9076   char *filename = getTapeFilename(nr);
9077
9078   InitTapeDirectory(leveldir_current->subdir);
9079
9080   SaveTapeExt(filename);
9081 }
9082
9083 void SaveScoreTape(int nr)
9084 {
9085   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9086
9087   // used instead of "leveldir_current->subdir" (for network games)
9088   InitScoreTapeDirectory(levelset.identifier, nr);
9089
9090   SaveTapeExt(filename);
9091 }
9092
9093 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9094                                   unsigned int req_state_added)
9095 {
9096   char *filename = getTapeFilename(nr);
9097   boolean new_tape = !fileExists(filename);
9098   boolean tape_saved = FALSE;
9099
9100   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9101   {
9102     SaveTape(nr);
9103
9104     if (new_tape)
9105       Request(msg_saved, REQ_CONFIRM | req_state_added);
9106
9107     tape_saved = TRUE;
9108   }
9109
9110   return tape_saved;
9111 }
9112
9113 boolean SaveTapeChecked(int nr)
9114 {
9115   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9116 }
9117
9118 boolean SaveTapeChecked_LevelSolved(int nr)
9119 {
9120   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9121                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9122 }
9123
9124 void DumpTape(struct TapeInfo *tape)
9125 {
9126   int tape_frame_counter;
9127   int i, j;
9128
9129   if (tape->no_valid_file)
9130   {
9131     Warn("cannot dump -- no valid tape file found");
9132
9133     return;
9134   }
9135
9136   PrintLine("-", 79);
9137
9138   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9139         tape->level_nr, tape->file_version, tape->game_version);
9140   Print("                  (effective engine version %08d)\n",
9141         tape->engine_version);
9142   Print("Level series identifier: '%s'\n", tape->level_identifier);
9143
9144   Print("Solution tape: %s\n",
9145         tape->solved ? "yes" :
9146         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9147
9148   Print("Special tape properties: ");
9149   if (tape->property_bits == TAPE_PROPERTY_NONE)
9150     Print("[none]");
9151   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9152     Print("[em_random_bug]");
9153   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9154     Print("[game_speed]");
9155   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9156     Print("[pause]");
9157   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9158     Print("[single_step]");
9159   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9160     Print("[snapshot]");
9161   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9162     Print("[replayed]");
9163   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9164     Print("[tas_keys]");
9165   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9166     Print("[small_graphics]");
9167   Print("\n");
9168
9169   int year2 = tape->date / 10000;
9170   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9171   int month_index_raw = (tape->date / 100) % 100;
9172   int month_index = month_index_raw % 12;       // prevent invalid index
9173   int month = month_index + 1;
9174   int day = tape->date % 100;
9175
9176   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9177
9178   PrintLine("-", 79);
9179
9180   tape_frame_counter = 0;
9181
9182   for (i = 0; i < tape->length; i++)
9183   {
9184     if (i >= MAX_TAPE_LEN)
9185       break;
9186
9187     Print("%04d: ", i);
9188
9189     for (j = 0; j < MAX_PLAYERS; j++)
9190     {
9191       if (tape->player_participates[j])
9192       {
9193         int action = tape->pos[i].action[j];
9194
9195         Print("%d:%02x ", j, action);
9196         Print("[%c%c%c%c|%c%c] - ",
9197               (action & JOY_LEFT ? '<' : ' '),
9198               (action & JOY_RIGHT ? '>' : ' '),
9199               (action & JOY_UP ? '^' : ' '),
9200               (action & JOY_DOWN ? 'v' : ' '),
9201               (action & JOY_BUTTON_1 ? '1' : ' '),
9202               (action & JOY_BUTTON_2 ? '2' : ' '));
9203       }
9204     }
9205
9206     Print("(%03d) ", tape->pos[i].delay);
9207     Print("[%05d]\n", tape_frame_counter);
9208
9209     tape_frame_counter += tape->pos[i].delay;
9210   }
9211
9212   PrintLine("-", 79);
9213 }
9214
9215 void DumpTapes(void)
9216 {
9217   static LevelDirTree *dumptape_leveldir = NULL;
9218
9219   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9220                                                 global.dumptape_leveldir);
9221
9222   if (dumptape_leveldir == NULL)
9223     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9224
9225   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9226       global.dumptape_level_nr > dumptape_leveldir->last_level)
9227     Fail("no such level number: %d", global.dumptape_level_nr);
9228
9229   leveldir_current = dumptape_leveldir;
9230
9231   if (options.mytapes)
9232     LoadTape(global.dumptape_level_nr);
9233   else
9234     LoadSolutionTape(global.dumptape_level_nr);
9235
9236   DumpTape(&tape);
9237
9238   CloseAllAndExit(0);
9239 }
9240
9241
9242 // ============================================================================
9243 // score file functions
9244 // ============================================================================
9245
9246 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9247 {
9248   int i;
9249
9250   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9251   {
9252     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9253     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9254     scores->entry[i].score = 0;
9255     scores->entry[i].time = 0;
9256
9257     scores->entry[i].id = -1;
9258     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9259     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9260     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9261     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9262     strcpy(scores->entry[i].country_code, "??");
9263   }
9264
9265   scores->num_entries = 0;
9266   scores->last_added = -1;
9267   scores->last_added_local = -1;
9268
9269   scores->updated = FALSE;
9270   scores->uploaded = FALSE;
9271   scores->tape_downloaded = FALSE;
9272   scores->force_last_added = FALSE;
9273
9274   // The following values are intentionally not reset here:
9275   // - last_level_nr
9276   // - last_entry_nr
9277   // - next_level_nr
9278   // - continue_playing
9279   // - continue_on_return
9280 }
9281
9282 static void setScoreInfoToDefaults(void)
9283 {
9284   setScoreInfoToDefaultsExt(&scores);
9285 }
9286
9287 static void setServerScoreInfoToDefaults(void)
9288 {
9289   setScoreInfoToDefaultsExt(&server_scores);
9290 }
9291
9292 static void LoadScore_OLD(int nr)
9293 {
9294   int i;
9295   char *filename = getScoreFilename(nr);
9296   char cookie[MAX_LINE_LEN];
9297   char line[MAX_LINE_LEN];
9298   char *line_ptr;
9299   FILE *file;
9300
9301   if (!(file = fopen(filename, MODE_READ)))
9302     return;
9303
9304   // check file identifier
9305   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9306     cookie[0] = '\0';
9307   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9308     cookie[strlen(cookie) - 1] = '\0';
9309
9310   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9311   {
9312     Warn("unknown format of score file '%s'", filename);
9313
9314     fclose(file);
9315
9316     return;
9317   }
9318
9319   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9320   {
9321     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9322       Warn("fscanf() failed; %s", strerror(errno));
9323
9324     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9325       line[0] = '\0';
9326
9327     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9328       line[strlen(line) - 1] = '\0';
9329
9330     for (line_ptr = line; *line_ptr; line_ptr++)
9331     {
9332       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9333       {
9334         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9335         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9336         break;
9337       }
9338     }
9339   }
9340
9341   fclose(file);
9342 }
9343
9344 static void ConvertScore_OLD(void)
9345 {
9346   // only convert score to time for levels that rate playing time over score
9347   if (!level.rate_time_over_score)
9348     return;
9349
9350   // convert old score to playing time for score-less levels (like Supaplex)
9351   int time_final_max = 999;
9352   int i;
9353
9354   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9355   {
9356     int score = scores.entry[i].score;
9357
9358     if (score > 0 && score < time_final_max)
9359       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9360   }
9361 }
9362
9363 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9364 {
9365   scores->file_version = getFileVersion(file);
9366   scores->game_version = getFileVersion(file);
9367
9368   return chunk_size;
9369 }
9370
9371 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9372 {
9373   char *level_identifier = NULL;
9374   int level_identifier_size;
9375   int i;
9376
9377   level_identifier_size = getFile16BitBE(file);
9378
9379   level_identifier = checked_malloc(level_identifier_size);
9380
9381   for (i = 0; i < level_identifier_size; i++)
9382     level_identifier[i] = getFile8Bit(file);
9383
9384   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9385   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9386
9387   checked_free(level_identifier);
9388
9389   scores->level_nr = getFile16BitBE(file);
9390   scores->num_entries = getFile16BitBE(file);
9391
9392   chunk_size = 2 + level_identifier_size + 2 + 2;
9393
9394   return chunk_size;
9395 }
9396
9397 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9398 {
9399   int i, j;
9400
9401   for (i = 0; i < scores->num_entries; i++)
9402   {
9403     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9404       scores->entry[i].name[j] = getFile8Bit(file);
9405
9406     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9407   }
9408
9409   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9410
9411   return chunk_size;
9412 }
9413
9414 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9415 {
9416   int i;
9417
9418   for (i = 0; i < scores->num_entries; i++)
9419     scores->entry[i].score = getFile16BitBE(file);
9420
9421   chunk_size = scores->num_entries * 2;
9422
9423   return chunk_size;
9424 }
9425
9426 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9427 {
9428   int i;
9429
9430   for (i = 0; i < scores->num_entries; i++)
9431     scores->entry[i].score = getFile32BitBE(file);
9432
9433   chunk_size = scores->num_entries * 4;
9434
9435   return chunk_size;
9436 }
9437
9438 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9439 {
9440   int i;
9441
9442   for (i = 0; i < scores->num_entries; i++)
9443     scores->entry[i].time = getFile32BitBE(file);
9444
9445   chunk_size = scores->num_entries * 4;
9446
9447   return chunk_size;
9448 }
9449
9450 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9451 {
9452   int i, j;
9453
9454   for (i = 0; i < scores->num_entries; i++)
9455   {
9456     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9457       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9458
9459     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9460   }
9461
9462   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9463
9464   return chunk_size;
9465 }
9466
9467 void LoadScore(int nr)
9468 {
9469   char *filename = getScoreFilename(nr);
9470   char cookie[MAX_LINE_LEN];
9471   char chunk_name[CHUNK_ID_LEN + 1];
9472   int chunk_size;
9473   boolean old_score_file_format = FALSE;
9474   File *file;
9475
9476   // always start with reliable default values
9477   setScoreInfoToDefaults();
9478
9479   if (!(file = openFile(filename, MODE_READ)))
9480     return;
9481
9482   getFileChunkBE(file, chunk_name, NULL);
9483   if (strEqual(chunk_name, "RND1"))
9484   {
9485     getFile32BitBE(file);               // not used
9486
9487     getFileChunkBE(file, chunk_name, NULL);
9488     if (!strEqual(chunk_name, "SCOR"))
9489     {
9490       Warn("unknown format of score file '%s'", filename);
9491
9492       closeFile(file);
9493
9494       return;
9495     }
9496   }
9497   else  // check for old file format with cookie string
9498   {
9499     strcpy(cookie, chunk_name);
9500     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9501       cookie[4] = '\0';
9502     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9503       cookie[strlen(cookie) - 1] = '\0';
9504
9505     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9506     {
9507       Warn("unknown format of score file '%s'", filename);
9508
9509       closeFile(file);
9510
9511       return;
9512     }
9513
9514     old_score_file_format = TRUE;
9515   }
9516
9517   if (old_score_file_format)
9518   {
9519     // score files from versions before 4.2.4.0 without chunk structure
9520     LoadScore_OLD(nr);
9521
9522     // convert score to time, if possible (mainly for Supaplex levels)
9523     ConvertScore_OLD();
9524   }
9525   else
9526   {
9527     static struct
9528     {
9529       char *name;
9530       int size;
9531       int (*loader)(File *, int, struct ScoreInfo *);
9532     }
9533     chunk_info[] =
9534     {
9535       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9536       { "INFO", -1,                     LoadScore_INFO },
9537       { "NAME", -1,                     LoadScore_NAME },
9538       { "SCOR", -1,                     LoadScore_SCOR },
9539       { "SC4R", -1,                     LoadScore_SC4R },
9540       { "TIME", -1,                     LoadScore_TIME },
9541       { "TAPE", -1,                     LoadScore_TAPE },
9542
9543       {  NULL,  0,                      NULL }
9544     };
9545
9546     while (getFileChunkBE(file, chunk_name, &chunk_size))
9547     {
9548       int i = 0;
9549
9550       while (chunk_info[i].name != NULL &&
9551              !strEqual(chunk_name, chunk_info[i].name))
9552         i++;
9553
9554       if (chunk_info[i].name == NULL)
9555       {
9556         Warn("unknown chunk '%s' in score file '%s'",
9557               chunk_name, filename);
9558
9559         ReadUnusedBytesFromFile(file, chunk_size);
9560       }
9561       else if (chunk_info[i].size != -1 &&
9562                chunk_info[i].size != chunk_size)
9563       {
9564         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9565               chunk_size, chunk_name, filename);
9566
9567         ReadUnusedBytesFromFile(file, chunk_size);
9568       }
9569       else
9570       {
9571         // call function to load this score chunk
9572         int chunk_size_expected =
9573           (chunk_info[i].loader)(file, chunk_size, &scores);
9574
9575         // the size of some chunks cannot be checked before reading other
9576         // chunks first (like "HEAD" and "BODY") that contain some header
9577         // information, so check them here
9578         if (chunk_size_expected != chunk_size)
9579         {
9580           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9581                 chunk_size, chunk_name, filename);
9582         }
9583       }
9584     }
9585   }
9586
9587   closeFile(file);
9588 }
9589
9590 #if ENABLE_HISTORIC_CHUNKS
9591 void SaveScore_OLD(int nr)
9592 {
9593   int i;
9594   char *filename = getScoreFilename(nr);
9595   FILE *file;
9596
9597   // used instead of "leveldir_current->subdir" (for network games)
9598   InitScoreDirectory(levelset.identifier);
9599
9600   if (!(file = fopen(filename, MODE_WRITE)))
9601   {
9602     Warn("cannot save score for level %d", nr);
9603
9604     return;
9605   }
9606
9607   fprintf(file, "%s\n\n", SCORE_COOKIE);
9608
9609   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9610     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9611
9612   fclose(file);
9613
9614   SetFilePermissions(filename, PERMS_PRIVATE);
9615 }
9616 #endif
9617
9618 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9619 {
9620   putFileVersion(file, scores->file_version);
9621   putFileVersion(file, scores->game_version);
9622 }
9623
9624 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9625 {
9626   int level_identifier_size = strlen(scores->level_identifier) + 1;
9627   int i;
9628
9629   putFile16BitBE(file, level_identifier_size);
9630
9631   for (i = 0; i < level_identifier_size; i++)
9632     putFile8Bit(file, scores->level_identifier[i]);
9633
9634   putFile16BitBE(file, scores->level_nr);
9635   putFile16BitBE(file, scores->num_entries);
9636 }
9637
9638 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9639 {
9640   int i, j;
9641
9642   for (i = 0; i < scores->num_entries; i++)
9643   {
9644     int name_size = strlen(scores->entry[i].name);
9645
9646     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9647       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9648   }
9649 }
9650
9651 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9652 {
9653   int i;
9654
9655   for (i = 0; i < scores->num_entries; i++)
9656     putFile16BitBE(file, scores->entry[i].score);
9657 }
9658
9659 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9660 {
9661   int i;
9662
9663   for (i = 0; i < scores->num_entries; i++)
9664     putFile32BitBE(file, scores->entry[i].score);
9665 }
9666
9667 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9668 {
9669   int i;
9670
9671   for (i = 0; i < scores->num_entries; i++)
9672     putFile32BitBE(file, scores->entry[i].time);
9673 }
9674
9675 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9676 {
9677   int i, j;
9678
9679   for (i = 0; i < scores->num_entries; i++)
9680   {
9681     int size = strlen(scores->entry[i].tape_basename);
9682
9683     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9684       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9685   }
9686 }
9687
9688 static void SaveScoreToFilename(char *filename)
9689 {
9690   FILE *file;
9691   int info_chunk_size;
9692   int name_chunk_size;
9693   int scor_chunk_size;
9694   int sc4r_chunk_size;
9695   int time_chunk_size;
9696   int tape_chunk_size;
9697   boolean has_large_score_values;
9698   int i;
9699
9700   if (!(file = fopen(filename, MODE_WRITE)))
9701   {
9702     Warn("cannot save score file '%s'", filename);
9703
9704     return;
9705   }
9706
9707   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9708   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9709   scor_chunk_size = scores.num_entries * 2;
9710   sc4r_chunk_size = scores.num_entries * 4;
9711   time_chunk_size = scores.num_entries * 4;
9712   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9713
9714   has_large_score_values = FALSE;
9715   for (i = 0; i < scores.num_entries; i++)
9716     if (scores.entry[i].score > 0xffff)
9717       has_large_score_values = TRUE;
9718
9719   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9720   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9721
9722   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9723   SaveScore_VERS(file, &scores);
9724
9725   putFileChunkBE(file, "INFO", info_chunk_size);
9726   SaveScore_INFO(file, &scores);
9727
9728   putFileChunkBE(file, "NAME", name_chunk_size);
9729   SaveScore_NAME(file, &scores);
9730
9731   if (has_large_score_values)
9732   {
9733     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9734     SaveScore_SC4R(file, &scores);
9735   }
9736   else
9737   {
9738     putFileChunkBE(file, "SCOR", scor_chunk_size);
9739     SaveScore_SCOR(file, &scores);
9740   }
9741
9742   putFileChunkBE(file, "TIME", time_chunk_size);
9743   SaveScore_TIME(file, &scores);
9744
9745   putFileChunkBE(file, "TAPE", tape_chunk_size);
9746   SaveScore_TAPE(file, &scores);
9747
9748   fclose(file);
9749
9750   SetFilePermissions(filename, PERMS_PRIVATE);
9751 }
9752
9753 void SaveScore(int nr)
9754 {
9755   char *filename = getScoreFilename(nr);
9756   int i;
9757
9758   // used instead of "leveldir_current->subdir" (for network games)
9759   InitScoreDirectory(levelset.identifier);
9760
9761   scores.file_version = FILE_VERSION_ACTUAL;
9762   scores.game_version = GAME_VERSION_ACTUAL;
9763
9764   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9765   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9766   scores.level_nr = level_nr;
9767
9768   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9769     if (scores.entry[i].score == 0 &&
9770         scores.entry[i].time == 0 &&
9771         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9772       break;
9773
9774   scores.num_entries = i;
9775
9776   if (scores.num_entries == 0)
9777     return;
9778
9779   SaveScoreToFilename(filename);
9780 }
9781
9782 static void LoadServerScoreFromCache(int nr)
9783 {
9784   struct ScoreEntry score_entry;
9785   struct
9786   {
9787     void *value;
9788     boolean is_string;
9789     int string_size;
9790   }
9791   score_mapping[] =
9792   {
9793     { &score_entry.score,               FALSE,  0                       },
9794     { &score_entry.time,                FALSE,  0                       },
9795     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
9796     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
9797     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
9798     { &score_entry.id,                  FALSE,  0                       },
9799     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
9800     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
9801     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
9802     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
9803
9804     { NULL,                             FALSE,  0                       }
9805   };
9806   char *filename = getScoreCacheFilename(nr);
9807   SetupFileHash *score_hash = loadSetupFileHash(filename);
9808   int i, j;
9809
9810   server_scores.num_entries = 0;
9811
9812   if (score_hash == NULL)
9813     return;
9814
9815   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9816   {
9817     score_entry = server_scores.entry[i];
9818
9819     for (j = 0; score_mapping[j].value != NULL; j++)
9820     {
9821       char token[10];
9822
9823       sprintf(token, "%02d.%d", i, j);
9824
9825       char *value = getHashEntry(score_hash, token);
9826
9827       if (value == NULL)
9828         continue;
9829
9830       if (score_mapping[j].is_string)
9831       {
9832         char *score_value = (char *)score_mapping[j].value;
9833         int value_size = score_mapping[j].string_size;
9834
9835         strncpy(score_value, value, value_size);
9836         score_value[value_size] = '\0';
9837       }
9838       else
9839       {
9840         int *score_value = (int *)score_mapping[j].value;
9841
9842         *score_value = atoi(value);
9843       }
9844
9845       server_scores.num_entries = i + 1;
9846     }
9847
9848     server_scores.entry[i] = score_entry;
9849   }
9850
9851   freeSetupFileHash(score_hash);
9852 }
9853
9854 void LoadServerScore(int nr, boolean download_score)
9855 {
9856   if (!setup.use_api_server)
9857     return;
9858
9859   // always start with reliable default values
9860   setServerScoreInfoToDefaults();
9861
9862   // 1st step: load server scores from cache file (which may not exist)
9863   // (this should prevent reading it while the thread is writing to it)
9864   LoadServerScoreFromCache(nr);
9865
9866   if (download_score && runtime.use_api_server)
9867   {
9868     // 2nd step: download server scores from score server to cache file
9869     // (as thread, as it might time out if the server is not reachable)
9870     ApiGetScoreAsThread(nr);
9871   }
9872 }
9873
9874 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9875 {
9876   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9877
9878   // if score tape not uploaded, ask for uploading missing tapes later
9879   if (!setup.has_remaining_tapes)
9880     setup.ask_for_remaining_tapes = TRUE;
9881
9882   setup.provide_uploading_tapes = TRUE;
9883   setup.has_remaining_tapes = TRUE;
9884
9885   SaveSetup_ServerSetup();
9886 }
9887
9888 void SaveServerScore(int nr, boolean tape_saved)
9889 {
9890   if (!runtime.use_api_server)
9891   {
9892     PrepareScoreTapesForUpload(leveldir_current->subdir);
9893
9894     return;
9895   }
9896
9897   ApiAddScoreAsThread(nr, tape_saved, NULL);
9898 }
9899
9900 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9901                              char *score_tape_filename)
9902 {
9903   if (!runtime.use_api_server)
9904     return;
9905
9906   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9907 }
9908
9909 void LoadLocalAndServerScore(int nr, boolean download_score)
9910 {
9911   int last_added_local = scores.last_added_local;
9912   boolean force_last_added = scores.force_last_added;
9913
9914   // needed if only showing server scores
9915   setScoreInfoToDefaults();
9916
9917   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9918     LoadScore(nr);
9919
9920   // restore last added local score entry (before merging server scores)
9921   scores.last_added = scores.last_added_local = last_added_local;
9922
9923   if (setup.use_api_server &&
9924       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9925   {
9926     // load server scores from cache file and trigger update from server
9927     LoadServerScore(nr, download_score);
9928
9929     // merge local scores with scores from server
9930     MergeServerScore();
9931   }
9932
9933   if (force_last_added)
9934     scores.force_last_added = force_last_added;
9935 }
9936
9937
9938 // ============================================================================
9939 // setup file functions
9940 // ============================================================================
9941
9942 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
9943
9944
9945 static struct TokenInfo global_setup_tokens[] =
9946 {
9947   {
9948     TYPE_STRING,
9949     &setup.player_name,                         "player_name"
9950   },
9951   {
9952     TYPE_SWITCH,
9953     &setup.multiple_users,                      "multiple_users"
9954   },
9955   {
9956     TYPE_SWITCH,
9957     &setup.sound,                               "sound"
9958   },
9959   {
9960     TYPE_SWITCH,
9961     &setup.sound_loops,                         "repeating_sound_loops"
9962   },
9963   {
9964     TYPE_SWITCH,
9965     &setup.sound_music,                         "background_music"
9966   },
9967   {
9968     TYPE_SWITCH,
9969     &setup.sound_simple,                        "simple_sound_effects"
9970   },
9971   {
9972     TYPE_SWITCH,
9973     &setup.toons,                               "toons"
9974   },
9975   {
9976     TYPE_SWITCH,
9977     &setup.global_animations,                   "global_animations"
9978   },
9979   {
9980     TYPE_SWITCH,
9981     &setup.scroll_delay,                        "scroll_delay"
9982   },
9983   {
9984     TYPE_SWITCH,
9985     &setup.forced_scroll_delay,                 "forced_scroll_delay"
9986   },
9987   {
9988     TYPE_INTEGER,
9989     &setup.scroll_delay_value,                  "scroll_delay_value"
9990   },
9991   {
9992     TYPE_STRING,
9993     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
9994   },
9995   {
9996     TYPE_INTEGER,
9997     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
9998   },
9999   {
10000     TYPE_SWITCH,
10001     &setup.fade_screens,                        "fade_screens"
10002   },
10003   {
10004     TYPE_SWITCH,
10005     &setup.autorecord,                          "automatic_tape_recording"
10006   },
10007   {
10008     TYPE_SWITCH,
10009     &setup.autorecord_after_replay,             "autorecord_after_replay"
10010   },
10011   {
10012     TYPE_SWITCH,
10013     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10014   },
10015   {
10016     TYPE_SWITCH,
10017     &setup.show_titlescreen,                    "show_titlescreen"
10018   },
10019   {
10020     TYPE_SWITCH,
10021     &setup.quick_doors,                         "quick_doors"
10022   },
10023   {
10024     TYPE_SWITCH,
10025     &setup.team_mode,                           "team_mode"
10026   },
10027   {
10028     TYPE_SWITCH,
10029     &setup.handicap,                            "handicap"
10030   },
10031   {
10032     TYPE_SWITCH,
10033     &setup.skip_levels,                         "skip_levels"
10034   },
10035   {
10036     TYPE_SWITCH,
10037     &setup.increment_levels,                    "increment_levels"
10038   },
10039   {
10040     TYPE_SWITCH,
10041     &setup.auto_play_next_level,                "auto_play_next_level"
10042   },
10043   {
10044     TYPE_SWITCH,
10045     &setup.count_score_after_game,              "count_score_after_game"
10046   },
10047   {
10048     TYPE_SWITCH,
10049     &setup.show_scores_after_game,              "show_scores_after_game"
10050   },
10051   {
10052     TYPE_SWITCH,
10053     &setup.time_limit,                          "time_limit"
10054   },
10055   {
10056     TYPE_SWITCH,
10057     &setup.fullscreen,                          "fullscreen"
10058   },
10059   {
10060     TYPE_INTEGER,
10061     &setup.window_scaling_percent,              "window_scaling_percent"
10062   },
10063   {
10064     TYPE_STRING,
10065     &setup.window_scaling_quality,              "window_scaling_quality"
10066   },
10067   {
10068     TYPE_STRING,
10069     &setup.screen_rendering_mode,               "screen_rendering_mode"
10070   },
10071   {
10072     TYPE_STRING,
10073     &setup.vsync_mode,                          "vsync_mode"
10074   },
10075   {
10076     TYPE_SWITCH,
10077     &setup.ask_on_escape,                       "ask_on_escape"
10078   },
10079   {
10080     TYPE_SWITCH,
10081     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10082   },
10083   {
10084     TYPE_SWITCH,
10085     &setup.ask_on_game_over,                    "ask_on_game_over"
10086   },
10087   {
10088     TYPE_SWITCH,
10089     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10090   },
10091   {
10092     TYPE_SWITCH,
10093     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10094   },
10095   {
10096     TYPE_SWITCH,
10097     &setup.quick_switch,                        "quick_player_switch"
10098   },
10099   {
10100     TYPE_SWITCH,
10101     &setup.input_on_focus,                      "input_on_focus"
10102   },
10103   {
10104     TYPE_SWITCH,
10105     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10106   },
10107   {
10108     TYPE_SWITCH,
10109     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10110   },
10111   {
10112     TYPE_SWITCH,
10113     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10114   },
10115   {
10116     TYPE_SWITCH,
10117     &setup.game_speed_extended,                 "game_speed_extended"
10118   },
10119   {
10120     TYPE_INTEGER,
10121     &setup.game_frame_delay,                    "game_frame_delay"
10122   },
10123   {
10124     TYPE_SWITCH,
10125     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10126   },
10127   {
10128     TYPE_SWITCH,
10129     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10130   },
10131   {
10132     TYPE_SWITCH,
10133     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10134   },
10135   {
10136     TYPE_SWITCH3,
10137     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10138   },
10139   {
10140     TYPE_SWITCH,
10141     &setup.sp_show_border_elements,             "sp_show_border_elements"
10142   },
10143   {
10144     TYPE_SWITCH,
10145     &setup.small_game_graphics,                 "small_game_graphics"
10146   },
10147   {
10148     TYPE_SWITCH,
10149     &setup.show_load_save_buttons,              "show_load_save_buttons"
10150   },
10151   {
10152     TYPE_SWITCH,
10153     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10154   },
10155   {
10156     TYPE_STRING,
10157     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10158   },
10159   {
10160     TYPE_STRING,
10161     &setup.graphics_set,                        "graphics_set"
10162   },
10163   {
10164     TYPE_STRING,
10165     &setup.sounds_set,                          "sounds_set"
10166   },
10167   {
10168     TYPE_STRING,
10169     &setup.music_set,                           "music_set"
10170   },
10171   {
10172     TYPE_SWITCH3,
10173     &setup.override_level_graphics,             "override_level_graphics"
10174   },
10175   {
10176     TYPE_SWITCH3,
10177     &setup.override_level_sounds,               "override_level_sounds"
10178   },
10179   {
10180     TYPE_SWITCH3,
10181     &setup.override_level_music,                "override_level_music"
10182   },
10183   {
10184     TYPE_INTEGER,
10185     &setup.volume_simple,                       "volume_simple"
10186   },
10187   {
10188     TYPE_INTEGER,
10189     &setup.volume_loops,                        "volume_loops"
10190   },
10191   {
10192     TYPE_INTEGER,
10193     &setup.volume_music,                        "volume_music"
10194   },
10195   {
10196     TYPE_SWITCH,
10197     &setup.network_mode,                        "network_mode"
10198   },
10199   {
10200     TYPE_PLAYER,
10201     &setup.network_player_nr,                   "network_player"
10202   },
10203   {
10204     TYPE_STRING,
10205     &setup.network_server_hostname,             "network_server_hostname"
10206   },
10207   {
10208     TYPE_STRING,
10209     &setup.touch.control_type,                  "touch.control_type"
10210   },
10211   {
10212     TYPE_INTEGER,
10213     &setup.touch.move_distance,                 "touch.move_distance"
10214   },
10215   {
10216     TYPE_INTEGER,
10217     &setup.touch.drop_distance,                 "touch.drop_distance"
10218   },
10219   {
10220     TYPE_INTEGER,
10221     &setup.touch.transparency,                  "touch.transparency"
10222   },
10223   {
10224     TYPE_INTEGER,
10225     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10226   },
10227   {
10228     TYPE_INTEGER,
10229     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10230   },
10231   {
10232     TYPE_INTEGER,
10233     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10234   },
10235   {
10236     TYPE_INTEGER,
10237     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10238   },
10239   {
10240     TYPE_INTEGER,
10241     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10242   },
10243   {
10244     TYPE_INTEGER,
10245     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10246   },
10247   {
10248     TYPE_SWITCH,
10249     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10250   },
10251 };
10252
10253 static struct TokenInfo auto_setup_tokens[] =
10254 {
10255   {
10256     TYPE_INTEGER,
10257     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10258   },
10259 };
10260
10261 static struct TokenInfo server_setup_tokens[] =
10262 {
10263   {
10264     TYPE_STRING,
10265     &setup.player_uuid,                         "player_uuid"
10266   },
10267   {
10268     TYPE_INTEGER,
10269     &setup.player_version,                      "player_version"
10270   },
10271   {
10272     TYPE_SWITCH,
10273     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10274   },
10275   {
10276     TYPE_STRING,
10277     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10278   },
10279   {
10280     TYPE_STRING,
10281     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10282   },
10283   {
10284     TYPE_SWITCH,
10285     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10286   },
10287   {
10288     TYPE_SWITCH,
10289     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10290   },
10291   {
10292     TYPE_SWITCH,
10293     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10294   },
10295   {
10296     TYPE_SWITCH,
10297     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10298   },
10299   {
10300     TYPE_SWITCH,
10301     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10302   },
10303 };
10304
10305 static struct TokenInfo editor_setup_tokens[] =
10306 {
10307   {
10308     TYPE_SWITCH,
10309     &setup.editor.el_classic,                   "editor.el_classic"
10310   },
10311   {
10312     TYPE_SWITCH,
10313     &setup.editor.el_custom,                    "editor.el_custom"
10314   },
10315   {
10316     TYPE_SWITCH,
10317     &setup.editor.el_user_defined,              "editor.el_user_defined"
10318   },
10319   {
10320     TYPE_SWITCH,
10321     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10322   },
10323   {
10324     TYPE_SWITCH,
10325     &setup.editor.el_headlines,                 "editor.el_headlines"
10326   },
10327   {
10328     TYPE_SWITCH,
10329     &setup.editor.show_element_token,           "editor.show_element_token"
10330   },
10331   {
10332     TYPE_SWITCH,
10333     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10334   },
10335 };
10336
10337 static struct TokenInfo editor_cascade_setup_tokens[] =
10338 {
10339   {
10340     TYPE_SWITCH,
10341     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10342   },
10343   {
10344     TYPE_SWITCH,
10345     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10346   },
10347   {
10348     TYPE_SWITCH,
10349     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10350   },
10351   {
10352     TYPE_SWITCH,
10353     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10354   },
10355   {
10356     TYPE_SWITCH,
10357     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10358   },
10359   {
10360     TYPE_SWITCH,
10361     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10362   },
10363   {
10364     TYPE_SWITCH,
10365     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10366   },
10367   {
10368     TYPE_SWITCH,
10369     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10370   },
10371   {
10372     TYPE_SWITCH,
10373     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10374   },
10375   {
10376     TYPE_SWITCH,
10377     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10378   },
10379   {
10380     TYPE_SWITCH,
10381     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10382   },
10383   {
10384     TYPE_SWITCH,
10385     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10386   },
10387   {
10388     TYPE_SWITCH,
10389     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10390   },
10391   {
10392     TYPE_SWITCH,
10393     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10394   },
10395   {
10396     TYPE_SWITCH,
10397     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10398   },
10399   {
10400     TYPE_SWITCH,
10401     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10402   },
10403   {
10404     TYPE_SWITCH,
10405     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10406   },
10407   {
10408     TYPE_SWITCH,
10409     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10410   },
10411   {
10412     TYPE_SWITCH,
10413     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10414   },
10415 };
10416
10417 static struct TokenInfo shortcut_setup_tokens[] =
10418 {
10419   {
10420     TYPE_KEY_X11,
10421     &setup.shortcut.save_game,                  "shortcut.save_game"
10422   },
10423   {
10424     TYPE_KEY_X11,
10425     &setup.shortcut.load_game,                  "shortcut.load_game"
10426   },
10427   {
10428     TYPE_KEY_X11,
10429     &setup.shortcut.restart_game,               "shortcut.restart_game"
10430   },
10431   {
10432     TYPE_KEY_X11,
10433     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10434   },
10435   {
10436     TYPE_KEY_X11,
10437     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10438   },
10439   {
10440     TYPE_KEY_X11,
10441     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10442   },
10443   {
10444     TYPE_KEY_X11,
10445     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10446   },
10447   {
10448     TYPE_KEY_X11,
10449     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10450   },
10451   {
10452     TYPE_KEY_X11,
10453     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10454   },
10455   {
10456     TYPE_KEY_X11,
10457     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10458   },
10459   {
10460     TYPE_KEY_X11,
10461     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10462   },
10463   {
10464     TYPE_KEY_X11,
10465     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10466   },
10467   {
10468     TYPE_KEY_X11,
10469     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10470   },
10471   {
10472     TYPE_KEY_X11,
10473     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10474   },
10475   {
10476     TYPE_KEY_X11,
10477     &setup.shortcut.tape_record,                "shortcut.tape_record"
10478   },
10479   {
10480     TYPE_KEY_X11,
10481     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10482   },
10483   {
10484     TYPE_KEY_X11,
10485     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10486   },
10487   {
10488     TYPE_KEY_X11,
10489     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10490   },
10491   {
10492     TYPE_KEY_X11,
10493     &setup.shortcut.sound_music,                "shortcut.sound_music"
10494   },
10495   {
10496     TYPE_KEY_X11,
10497     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10498   },
10499   {
10500     TYPE_KEY_X11,
10501     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10502   },
10503   {
10504     TYPE_KEY_X11,
10505     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10506   },
10507   {
10508     TYPE_KEY_X11,
10509     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10510   },
10511 };
10512
10513 static struct SetupInputInfo setup_input;
10514 static struct TokenInfo player_setup_tokens[] =
10515 {
10516   {
10517     TYPE_BOOLEAN,
10518     &setup_input.use_joystick,                  ".use_joystick"
10519   },
10520   {
10521     TYPE_STRING,
10522     &setup_input.joy.device_name,               ".joy.device_name"
10523   },
10524   {
10525     TYPE_INTEGER,
10526     &setup_input.joy.xleft,                     ".joy.xleft"
10527   },
10528   {
10529     TYPE_INTEGER,
10530     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10531   },
10532   {
10533     TYPE_INTEGER,
10534     &setup_input.joy.xright,                    ".joy.xright"
10535   },
10536   {
10537     TYPE_INTEGER,
10538     &setup_input.joy.yupper,                    ".joy.yupper"
10539   },
10540   {
10541     TYPE_INTEGER,
10542     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10543   },
10544   {
10545     TYPE_INTEGER,
10546     &setup_input.joy.ylower,                    ".joy.ylower"
10547   },
10548   {
10549     TYPE_INTEGER,
10550     &setup_input.joy.snap,                      ".joy.snap_field"
10551   },
10552   {
10553     TYPE_INTEGER,
10554     &setup_input.joy.drop,                      ".joy.place_bomb"
10555   },
10556   {
10557     TYPE_KEY_X11,
10558     &setup_input.key.left,                      ".key.move_left"
10559   },
10560   {
10561     TYPE_KEY_X11,
10562     &setup_input.key.right,                     ".key.move_right"
10563   },
10564   {
10565     TYPE_KEY_X11,
10566     &setup_input.key.up,                        ".key.move_up"
10567   },
10568   {
10569     TYPE_KEY_X11,
10570     &setup_input.key.down,                      ".key.move_down"
10571   },
10572   {
10573     TYPE_KEY_X11,
10574     &setup_input.key.snap,                      ".key.snap_field"
10575   },
10576   {
10577     TYPE_KEY_X11,
10578     &setup_input.key.drop,                      ".key.place_bomb"
10579   },
10580 };
10581
10582 static struct TokenInfo system_setup_tokens[] =
10583 {
10584   {
10585     TYPE_STRING,
10586     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10587   },
10588   {
10589     TYPE_STRING,
10590     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10591   },
10592   {
10593     TYPE_STRING,
10594     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10595   },
10596   {
10597     TYPE_INTEGER,
10598     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10599   },
10600 };
10601
10602 static struct TokenInfo internal_setup_tokens[] =
10603 {
10604   {
10605     TYPE_STRING,
10606     &setup.internal.program_title,              "program_title"
10607   },
10608   {
10609     TYPE_STRING,
10610     &setup.internal.program_version,            "program_version"
10611   },
10612   {
10613     TYPE_STRING,
10614     &setup.internal.program_author,             "program_author"
10615   },
10616   {
10617     TYPE_STRING,
10618     &setup.internal.program_email,              "program_email"
10619   },
10620   {
10621     TYPE_STRING,
10622     &setup.internal.program_website,            "program_website"
10623   },
10624   {
10625     TYPE_STRING,
10626     &setup.internal.program_copyright,          "program_copyright"
10627   },
10628   {
10629     TYPE_STRING,
10630     &setup.internal.program_company,            "program_company"
10631   },
10632   {
10633     TYPE_STRING,
10634     &setup.internal.program_icon_file,          "program_icon_file"
10635   },
10636   {
10637     TYPE_STRING,
10638     &setup.internal.default_graphics_set,       "default_graphics_set"
10639   },
10640   {
10641     TYPE_STRING,
10642     &setup.internal.default_sounds_set,         "default_sounds_set"
10643   },
10644   {
10645     TYPE_STRING,
10646     &setup.internal.default_music_set,          "default_music_set"
10647   },
10648   {
10649     TYPE_STRING,
10650     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10651   },
10652   {
10653     TYPE_STRING,
10654     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10655   },
10656   {
10657     TYPE_STRING,
10658     &setup.internal.fallback_music_file,        "fallback_music_file"
10659   },
10660   {
10661     TYPE_STRING,
10662     &setup.internal.default_level_series,       "default_level_series"
10663   },
10664   {
10665     TYPE_INTEGER,
10666     &setup.internal.default_window_width,       "default_window_width"
10667   },
10668   {
10669     TYPE_INTEGER,
10670     &setup.internal.default_window_height,      "default_window_height"
10671   },
10672   {
10673     TYPE_BOOLEAN,
10674     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10675   },
10676   {
10677     TYPE_BOOLEAN,
10678     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10679   },
10680   {
10681     TYPE_BOOLEAN,
10682     &setup.internal.create_user_levelset,       "create_user_levelset"
10683   },
10684   {
10685     TYPE_BOOLEAN,
10686     &setup.internal.info_screens_from_main,     "info_screens_from_main"
10687   },
10688   {
10689     TYPE_BOOLEAN,
10690     &setup.internal.menu_game,                  "menu_game"
10691   },
10692   {
10693     TYPE_BOOLEAN,
10694     &setup.internal.menu_engines,               "menu_engines"
10695   },
10696   {
10697     TYPE_BOOLEAN,
10698     &setup.internal.menu_editor,                "menu_editor"
10699   },
10700   {
10701     TYPE_BOOLEAN,
10702     &setup.internal.menu_graphics,              "menu_graphics"
10703   },
10704   {
10705     TYPE_BOOLEAN,
10706     &setup.internal.menu_sound,                 "menu_sound"
10707   },
10708   {
10709     TYPE_BOOLEAN,
10710     &setup.internal.menu_artwork,               "menu_artwork"
10711   },
10712   {
10713     TYPE_BOOLEAN,
10714     &setup.internal.menu_input,                 "menu_input"
10715   },
10716   {
10717     TYPE_BOOLEAN,
10718     &setup.internal.menu_touch,                 "menu_touch"
10719   },
10720   {
10721     TYPE_BOOLEAN,
10722     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10723   },
10724   {
10725     TYPE_BOOLEAN,
10726     &setup.internal.menu_exit,                  "menu_exit"
10727   },
10728   {
10729     TYPE_BOOLEAN,
10730     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10731   },
10732   {
10733     TYPE_BOOLEAN,
10734     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
10735   },
10736   {
10737     TYPE_BOOLEAN,
10738     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
10739   },
10740   {
10741     TYPE_BOOLEAN,
10742     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
10743   },
10744   {
10745     TYPE_BOOLEAN,
10746     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
10747   },
10748   {
10749     TYPE_BOOLEAN,
10750     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
10751   },
10752   {
10753     TYPE_BOOLEAN,
10754     &setup.internal.info_title,                 "info_title"
10755   },
10756   {
10757     TYPE_BOOLEAN,
10758     &setup.internal.info_elements,              "info_elements"
10759   },
10760   {
10761     TYPE_BOOLEAN,
10762     &setup.internal.info_music,                 "info_music"
10763   },
10764   {
10765     TYPE_BOOLEAN,
10766     &setup.internal.info_credits,               "info_credits"
10767   },
10768   {
10769     TYPE_BOOLEAN,
10770     &setup.internal.info_program,               "info_program"
10771   },
10772   {
10773     TYPE_BOOLEAN,
10774     &setup.internal.info_version,               "info_version"
10775   },
10776   {
10777     TYPE_BOOLEAN,
10778     &setup.internal.info_levelset,              "info_levelset"
10779   },
10780   {
10781     TYPE_BOOLEAN,
10782     &setup.internal.info_exit,                  "info_exit"
10783   },
10784 };
10785
10786 static struct TokenInfo debug_setup_tokens[] =
10787 {
10788   {
10789     TYPE_INTEGER,
10790     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10791   },
10792   {
10793     TYPE_INTEGER,
10794     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10795   },
10796   {
10797     TYPE_INTEGER,
10798     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10799   },
10800   {
10801     TYPE_INTEGER,
10802     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10803   },
10804   {
10805     TYPE_INTEGER,
10806     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10807   },
10808   {
10809     TYPE_INTEGER,
10810     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
10811   },
10812   {
10813     TYPE_INTEGER,
10814     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
10815   },
10816   {
10817     TYPE_INTEGER,
10818     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
10819   },
10820   {
10821     TYPE_INTEGER,
10822     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
10823   },
10824   {
10825     TYPE_INTEGER,
10826     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
10827   },
10828   {
10829     TYPE_KEY_X11,
10830     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
10831   },
10832   {
10833     TYPE_KEY_X11,
10834     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
10835   },
10836   {
10837     TYPE_KEY_X11,
10838     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
10839   },
10840   {
10841     TYPE_KEY_X11,
10842     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
10843   },
10844   {
10845     TYPE_KEY_X11,
10846     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
10847   },
10848   {
10849     TYPE_KEY_X11,
10850     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
10851   },
10852   {
10853     TYPE_KEY_X11,
10854     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
10855   },
10856   {
10857     TYPE_KEY_X11,
10858     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
10859   },
10860   {
10861     TYPE_KEY_X11,
10862     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
10863   },
10864   {
10865     TYPE_KEY_X11,
10866     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
10867   },
10868   {
10869     TYPE_BOOLEAN,
10870     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
10871   {
10872     TYPE_BOOLEAN,
10873     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
10874   },
10875   {
10876     TYPE_BOOLEAN,
10877     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
10878   },
10879   {
10880     TYPE_SWITCH3,
10881     &setup.debug.xsn_mode,                      "debug.xsn_mode"
10882   },
10883   {
10884     TYPE_INTEGER,
10885     &setup.debug.xsn_percent,                   "debug.xsn_percent"
10886   },
10887 };
10888
10889 static struct TokenInfo options_setup_tokens[] =
10890 {
10891   {
10892     TYPE_BOOLEAN,
10893     &setup.options.verbose,                     "options.verbose"
10894   },
10895   {
10896     TYPE_BOOLEAN,
10897     &setup.options.debug,                       "options.debug"
10898   },
10899   {
10900     TYPE_STRING,
10901     &setup.options.debug_mode,                  "options.debug_mode"
10902   },
10903 };
10904
10905 static void setSetupInfoToDefaults(struct SetupInfo *si)
10906 {
10907   int i;
10908
10909   si->player_name = getStringCopy(getDefaultUserName(user.nr));
10910
10911   si->multiple_users = TRUE;
10912
10913   si->sound = TRUE;
10914   si->sound_loops = TRUE;
10915   si->sound_music = TRUE;
10916   si->sound_simple = TRUE;
10917   si->toons = TRUE;
10918   si->global_animations = TRUE;
10919   si->scroll_delay = TRUE;
10920   si->forced_scroll_delay = FALSE;
10921   si->scroll_delay_value = STD_SCROLL_DELAY;
10922   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10923   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10924   si->fade_screens = TRUE;
10925   si->autorecord = TRUE;
10926   si->autorecord_after_replay = TRUE;
10927   si->auto_pause_on_start = FALSE;
10928   si->show_titlescreen = TRUE;
10929   si->quick_doors = FALSE;
10930   si->team_mode = FALSE;
10931   si->handicap = TRUE;
10932   si->skip_levels = TRUE;
10933   si->increment_levels = TRUE;
10934   si->auto_play_next_level = TRUE;
10935   si->count_score_after_game = TRUE;
10936   si->show_scores_after_game = TRUE;
10937   si->time_limit = TRUE;
10938   si->fullscreen = FALSE;
10939   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10940   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10941   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10942   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10943   si->ask_on_escape = TRUE;
10944   si->ask_on_escape_editor = TRUE;
10945   si->ask_on_game_over = TRUE;
10946   si->ask_on_quit_game = TRUE;
10947   si->ask_on_quit_program = TRUE;
10948   si->quick_switch = FALSE;
10949   si->input_on_focus = FALSE;
10950   si->prefer_aga_graphics = TRUE;
10951   si->prefer_lowpass_sounds = FALSE;
10952   si->prefer_extra_panel_items = TRUE;
10953   si->game_speed_extended = FALSE;
10954   si->game_frame_delay = GAME_FRAME_DELAY;
10955   si->bd_skip_uncovering = FALSE;
10956   si->bd_skip_hatching = FALSE;
10957   si->bd_scroll_delay = TRUE;
10958   si->bd_smooth_movements = AUTO;
10959   si->sp_show_border_elements = FALSE;
10960   si->small_game_graphics = FALSE;
10961   si->show_load_save_buttons = FALSE;
10962   si->show_undo_redo_buttons = FALSE;
10963   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10964
10965   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10966   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
10967   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
10968
10969   si->override_level_graphics = FALSE;
10970   si->override_level_sounds = FALSE;
10971   si->override_level_music = FALSE;
10972
10973   si->volume_simple = 100;              // percent
10974   si->volume_loops = 100;               // percent
10975   si->volume_music = 100;               // percent
10976
10977   si->network_mode = FALSE;
10978   si->network_player_nr = 0;            // first player
10979   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10980
10981   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10982   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
10983   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
10984   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
10985   si->touch.draw_outlined = TRUE;
10986   si->touch.draw_pressed = TRUE;
10987
10988   for (i = 0; i < 2; i++)
10989   {
10990     char *default_grid_button[6][2] =
10991     {
10992       { "      ", "  ^^  " },
10993       { "      ", "  ^^  " },
10994       { "      ", "<<  >>" },
10995       { "      ", "<<  >>" },
10996       { "111222", "  vv  " },
10997       { "111222", "  vv  " }
10998     };
10999     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11000     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11001     int min_xsize = MIN(6, grid_xsize);
11002     int min_ysize = MIN(6, grid_ysize);
11003     int startx = grid_xsize - min_xsize;
11004     int starty = grid_ysize - min_ysize;
11005     int x, y;
11006
11007     // virtual buttons grid can only be set to defaults if video is initialized
11008     // (this will be repeated if virtual buttons are not loaded from setup file)
11009     if (video.initialized)
11010     {
11011       si->touch.grid_xsize[i] = grid_xsize;
11012       si->touch.grid_ysize[i] = grid_ysize;
11013     }
11014     else
11015     {
11016       si->touch.grid_xsize[i] = -1;
11017       si->touch.grid_ysize[i] = -1;
11018     }
11019
11020     for (x = 0; x < MAX_GRID_XSIZE; x++)
11021       for (y = 0; y < MAX_GRID_YSIZE; y++)
11022         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11023
11024     for (x = 0; x < min_xsize; x++)
11025       for (y = 0; y < min_ysize; y++)
11026         si->touch.grid_button[i][x][starty + y] =
11027           default_grid_button[y][0][x];
11028
11029     for (x = 0; x < min_xsize; x++)
11030       for (y = 0; y < min_ysize; y++)
11031         si->touch.grid_button[i][startx + x][starty + y] =
11032           default_grid_button[y][1][x];
11033   }
11034
11035   si->touch.grid_initialized            = video.initialized;
11036
11037   si->touch.overlay_buttons             = FALSE;
11038
11039   si->editor.el_boulderdash             = TRUE;
11040   si->editor.el_boulderdash_native      = TRUE;
11041   si->editor.el_emerald_mine            = TRUE;
11042   si->editor.el_emerald_mine_club       = TRUE;
11043   si->editor.el_more                    = TRUE;
11044   si->editor.el_sokoban                 = TRUE;
11045   si->editor.el_supaplex                = TRUE;
11046   si->editor.el_diamond_caves           = TRUE;
11047   si->editor.el_dx_boulderdash          = TRUE;
11048
11049   si->editor.el_mirror_magic            = TRUE;
11050   si->editor.el_deflektor               = TRUE;
11051
11052   si->editor.el_chars                   = TRUE;
11053   si->editor.el_steel_chars             = TRUE;
11054
11055   si->editor.el_classic                 = TRUE;
11056   si->editor.el_custom                  = TRUE;
11057
11058   si->editor.el_user_defined            = FALSE;
11059   si->editor.el_dynamic                 = TRUE;
11060
11061   si->editor.el_headlines               = TRUE;
11062
11063   si->editor.show_element_token         = FALSE;
11064
11065   si->editor.show_read_only_warning     = TRUE;
11066
11067   si->editor.use_template_for_new_levels = TRUE;
11068
11069   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11070   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11071   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11072   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11073   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11074
11075   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11076   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11077   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11078   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11079   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11080
11081   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11082   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11083   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11084   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11085   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11086   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11087
11088   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11089   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11090   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11091
11092   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11093   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11094   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11095   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11096
11097   for (i = 0; i < MAX_PLAYERS; i++)
11098   {
11099     si->input[i].use_joystick = FALSE;
11100     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11101     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11102     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11103     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11104     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11105     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11106     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11107     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11108     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11109     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11110     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11111     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11112     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11113     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11114     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11115   }
11116
11117   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11118   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11119   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11120   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11121
11122   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11123   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11124   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11125   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11126   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11127   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11128   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11129
11130   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11131
11132   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11133   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11134   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11135
11136   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11137   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11138   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11139
11140   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11141   si->internal.choose_from_top_leveldir = FALSE;
11142   si->internal.show_scaling_in_title = TRUE;
11143   si->internal.create_user_levelset = TRUE;
11144   si->internal.info_screens_from_main = FALSE;
11145
11146   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11147   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11148
11149   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11150   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11151   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11152   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11153   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11154   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11155   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11156   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11157   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11158   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11159
11160   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11161   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11162   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11163   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11164   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11165   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11166   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11167   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11168   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11169   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11170
11171   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11172   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11173
11174   si->debug.show_frames_per_second = FALSE;
11175
11176   si->debug.xsn_mode = AUTO;
11177   si->debug.xsn_percent = 0;
11178
11179   si->options.verbose = FALSE;
11180   si->options.debug = FALSE;
11181   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11182
11183 #if defined(PLATFORM_ANDROID)
11184   si->fullscreen = TRUE;
11185   si->touch.overlay_buttons = TRUE;
11186 #endif
11187
11188   setHideSetupEntry(&setup.debug.xsn_mode);
11189 }
11190
11191 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11192 {
11193   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11194 }
11195
11196 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11197 {
11198   si->player_uuid = NULL;       // (will be set later)
11199   si->player_version = 1;       // (will be set later)
11200
11201   si->use_api_server = TRUE;
11202   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11203   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11204   si->ask_for_uploading_tapes = TRUE;
11205   si->ask_for_remaining_tapes = FALSE;
11206   si->provide_uploading_tapes = TRUE;
11207   si->ask_for_using_api_server = TRUE;
11208   si->has_remaining_tapes = FALSE;
11209 }
11210
11211 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11212 {
11213   si->editor_cascade.el_bd              = TRUE;
11214   si->editor_cascade.el_bd_native       = TRUE;
11215   si->editor_cascade.el_em              = TRUE;
11216   si->editor_cascade.el_emc             = TRUE;
11217   si->editor_cascade.el_rnd             = TRUE;
11218   si->editor_cascade.el_sb              = TRUE;
11219   si->editor_cascade.el_sp              = TRUE;
11220   si->editor_cascade.el_dc              = TRUE;
11221   si->editor_cascade.el_dx              = TRUE;
11222
11223   si->editor_cascade.el_mm              = TRUE;
11224   si->editor_cascade.el_df              = TRUE;
11225
11226   si->editor_cascade.el_chars           = FALSE;
11227   si->editor_cascade.el_steel_chars     = FALSE;
11228   si->editor_cascade.el_ce              = FALSE;
11229   si->editor_cascade.el_ge              = FALSE;
11230   si->editor_cascade.el_es              = FALSE;
11231   si->editor_cascade.el_ref             = FALSE;
11232   si->editor_cascade.el_user            = FALSE;
11233   si->editor_cascade.el_dynamic         = FALSE;
11234 }
11235
11236 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11237
11238 static char *getHideSetupToken(void *setup_value)
11239 {
11240   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11241
11242   if (setup_value != NULL)
11243     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11244
11245   return hide_setup_token;
11246 }
11247
11248 void setHideSetupEntry(void *setup_value)
11249 {
11250   char *hide_setup_token = getHideSetupToken(setup_value);
11251
11252   if (hide_setup_hash == NULL)
11253     hide_setup_hash = newSetupFileHash();
11254
11255   if (setup_value != NULL)
11256     setHashEntry(hide_setup_hash, hide_setup_token, "");
11257 }
11258
11259 void removeHideSetupEntry(void *setup_value)
11260 {
11261   char *hide_setup_token = getHideSetupToken(setup_value);
11262
11263   if (setup_value != NULL)
11264     removeHashEntry(hide_setup_hash, hide_setup_token);
11265 }
11266
11267 boolean hideSetupEntry(void *setup_value)
11268 {
11269   char *hide_setup_token = getHideSetupToken(setup_value);
11270
11271   return (setup_value != NULL &&
11272           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11273 }
11274
11275 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11276                                       struct TokenInfo *token_info,
11277                                       int token_nr, char *token_text)
11278 {
11279   char *token_hide_text = getStringCat2(token_text, ".hide");
11280   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11281
11282   // set the value of this setup option in the setup option structure
11283   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11284
11285   // check if this setup option should be hidden in the setup menu
11286   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11287     setHideSetupEntry(token_info[token_nr].value);
11288
11289   free(token_hide_text);
11290 }
11291
11292 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11293                                       struct TokenInfo *token_info,
11294                                       int token_nr)
11295 {
11296   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11297                             token_info[token_nr].text);
11298 }
11299
11300 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11301 {
11302   int i, pnr;
11303
11304   if (!setup_file_hash)
11305     return;
11306
11307   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11308     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11309
11310   setup.touch.grid_initialized = TRUE;
11311   for (i = 0; i < 2; i++)
11312   {
11313     int grid_xsize = setup.touch.grid_xsize[i];
11314     int grid_ysize = setup.touch.grid_ysize[i];
11315     int x, y;
11316
11317     // if virtual buttons are not loaded from setup file, repeat initializing
11318     // virtual buttons grid with default values later when video is initialized
11319     if (grid_xsize == -1 ||
11320         grid_ysize == -1)
11321     {
11322       setup.touch.grid_initialized = FALSE;
11323
11324       continue;
11325     }
11326
11327     for (y = 0; y < grid_ysize; y++)
11328     {
11329       char token_string[MAX_LINE_LEN];
11330
11331       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11332
11333       char *value_string = getHashEntry(setup_file_hash, token_string);
11334
11335       if (value_string == NULL)
11336         continue;
11337
11338       for (x = 0; x < grid_xsize; x++)
11339       {
11340         char c = value_string[x];
11341
11342         setup.touch.grid_button[i][x][y] =
11343           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11344       }
11345     }
11346   }
11347
11348   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11349     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11350
11351   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11352     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11353
11354   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11355   {
11356     char prefix[30];
11357
11358     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11359
11360     setup_input = setup.input[pnr];
11361     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11362     {
11363       char full_token[100];
11364
11365       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11366       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11367                                 full_token);
11368     }
11369     setup.input[pnr] = setup_input;
11370   }
11371
11372   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11373     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11374
11375   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11376     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11377
11378   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11379     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11380
11381   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11382     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11383
11384   setHideRelatedSetupEntries();
11385 }
11386
11387 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11388 {
11389   int i;
11390
11391   if (!setup_file_hash)
11392     return;
11393
11394   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11395     setSetupInfo(auto_setup_tokens, i,
11396                  getHashEntry(setup_file_hash,
11397                               auto_setup_tokens[i].text));
11398 }
11399
11400 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11401 {
11402   int i;
11403
11404   if (!setup_file_hash)
11405     return;
11406
11407   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11408     setSetupInfo(server_setup_tokens, i,
11409                  getHashEntry(setup_file_hash,
11410                               server_setup_tokens[i].text));
11411 }
11412
11413 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11414 {
11415   int i;
11416
11417   if (!setup_file_hash)
11418     return;
11419
11420   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11421     setSetupInfo(editor_cascade_setup_tokens, i,
11422                  getHashEntry(setup_file_hash,
11423                               editor_cascade_setup_tokens[i].text));
11424 }
11425
11426 void LoadUserNames(void)
11427 {
11428   int last_user_nr = user.nr;
11429   int i;
11430
11431   if (global.user_names != NULL)
11432   {
11433     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11434       checked_free(global.user_names[i]);
11435
11436     checked_free(global.user_names);
11437   }
11438
11439   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11440
11441   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11442   {
11443     user.nr = i;
11444
11445     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11446
11447     if (setup_file_hash)
11448     {
11449       char *player_name = getHashEntry(setup_file_hash, "player_name");
11450
11451       global.user_names[i] = getFixedUserName(player_name);
11452
11453       freeSetupFileHash(setup_file_hash);
11454     }
11455
11456     if (global.user_names[i] == NULL)
11457       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11458   }
11459
11460   user.nr = last_user_nr;
11461 }
11462
11463 void LoadSetupFromFilename(char *filename)
11464 {
11465   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11466
11467   if (setup_file_hash)
11468   {
11469     decodeSetupFileHash_Default(setup_file_hash);
11470
11471     freeSetupFileHash(setup_file_hash);
11472   }
11473   else
11474   {
11475     Debug("setup", "using default setup values");
11476   }
11477 }
11478
11479 static void LoadSetup_SpecialPostProcessing(void)
11480 {
11481   char *player_name_new;
11482
11483   // needed to work around problems with fixed length strings
11484   player_name_new = getFixedUserName(setup.player_name);
11485   free(setup.player_name);
11486   setup.player_name = player_name_new;
11487
11488   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11489   if (setup.scroll_delay == FALSE)
11490   {
11491     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11492     setup.scroll_delay = TRUE;                  // now always "on"
11493   }
11494
11495   // make sure that scroll delay value stays inside valid range
11496   setup.scroll_delay_value =
11497     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11498 }
11499
11500 void LoadSetup_Default(void)
11501 {
11502   char *filename;
11503
11504   // always start with reliable default values
11505   setSetupInfoToDefaults(&setup);
11506
11507   // try to load setup values from default setup file
11508   filename = getDefaultSetupFilename();
11509
11510   if (fileExists(filename))
11511     LoadSetupFromFilename(filename);
11512
11513   // try to load setup values from platform setup file
11514   filename = getPlatformSetupFilename();
11515
11516   if (fileExists(filename))
11517     LoadSetupFromFilename(filename);
11518
11519   // try to load setup values from user setup file
11520   filename = getSetupFilename();
11521
11522   LoadSetupFromFilename(filename);
11523
11524   LoadSetup_SpecialPostProcessing();
11525 }
11526
11527 void LoadSetup_AutoSetup(void)
11528 {
11529   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11530   SetupFileHash *setup_file_hash = NULL;
11531
11532   // always start with reliable default values
11533   setSetupInfoToDefaults_AutoSetup(&setup);
11534
11535   setup_file_hash = loadSetupFileHash(filename);
11536
11537   if (setup_file_hash)
11538   {
11539     decodeSetupFileHash_AutoSetup(setup_file_hash);
11540
11541     freeSetupFileHash(setup_file_hash);
11542   }
11543
11544   free(filename);
11545 }
11546
11547 void LoadSetup_ServerSetup(void)
11548 {
11549   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11550   SetupFileHash *setup_file_hash = NULL;
11551
11552   // always start with reliable default values
11553   setSetupInfoToDefaults_ServerSetup(&setup);
11554
11555   setup_file_hash = loadSetupFileHash(filename);
11556
11557   if (setup_file_hash)
11558   {
11559     decodeSetupFileHash_ServerSetup(setup_file_hash);
11560
11561     freeSetupFileHash(setup_file_hash);
11562   }
11563
11564   free(filename);
11565
11566   if (setup.player_uuid == NULL)
11567   {
11568     // player UUID does not yet exist in setup file
11569     setup.player_uuid = getStringCopy(getUUID());
11570     setup.player_version = 2;
11571
11572     SaveSetup_ServerSetup();
11573   }
11574 }
11575
11576 void LoadSetup_EditorCascade(void)
11577 {
11578   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11579   SetupFileHash *setup_file_hash = NULL;
11580
11581   // always start with reliable default values
11582   setSetupInfoToDefaults_EditorCascade(&setup);
11583
11584   setup_file_hash = loadSetupFileHash(filename);
11585
11586   if (setup_file_hash)
11587   {
11588     decodeSetupFileHash_EditorCascade(setup_file_hash);
11589
11590     freeSetupFileHash(setup_file_hash);
11591   }
11592
11593   free(filename);
11594 }
11595
11596 void LoadSetup(void)
11597 {
11598   LoadSetup_Default();
11599   LoadSetup_AutoSetup();
11600   LoadSetup_ServerSetup();
11601   LoadSetup_EditorCascade();
11602 }
11603
11604 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11605                                            char *mapping_line)
11606 {
11607   char mapping_guid[MAX_LINE_LEN];
11608   char *mapping_start, *mapping_end;
11609
11610   // get GUID from game controller mapping line: copy complete line
11611   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11612   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11613
11614   // get GUID from game controller mapping line: cut after GUID part
11615   mapping_start = strchr(mapping_guid, ',');
11616   if (mapping_start != NULL)
11617     *mapping_start = '\0';
11618
11619   // cut newline from game controller mapping line
11620   mapping_end = strchr(mapping_line, '\n');
11621   if (mapping_end != NULL)
11622     *mapping_end = '\0';
11623
11624   // add mapping entry to game controller mappings hash
11625   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11626 }
11627
11628 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11629                                                  char *filename)
11630 {
11631   FILE *file;
11632
11633   if (!(file = fopen(filename, MODE_READ)))
11634   {
11635     Warn("cannot read game controller mappings file '%s'", filename);
11636
11637     return;
11638   }
11639
11640   while (!feof(file))
11641   {
11642     char line[MAX_LINE_LEN];
11643
11644     if (!fgets(line, MAX_LINE_LEN, file))
11645       break;
11646
11647     addGameControllerMappingToHash(mappings_hash, line);
11648   }
11649
11650   fclose(file);
11651 }
11652
11653 void SaveSetup_Default(void)
11654 {
11655   char *filename = getSetupFilename();
11656   FILE *file;
11657   int i, pnr;
11658
11659   InitUserDataDirectory();
11660
11661   if (!(file = fopen(filename, MODE_WRITE)))
11662   {
11663     Warn("cannot write setup file '%s'", filename);
11664
11665     return;
11666   }
11667
11668   fprintFileHeader(file, SETUP_FILENAME);
11669
11670   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11671   {
11672     // just to make things nicer :)
11673     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11674         global_setup_tokens[i].value == &setup.sound                    ||
11675         global_setup_tokens[i].value == &setup.graphics_set             ||
11676         global_setup_tokens[i].value == &setup.volume_simple            ||
11677         global_setup_tokens[i].value == &setup.network_mode             ||
11678         global_setup_tokens[i].value == &setup.touch.control_type       ||
11679         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11680         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11681       fprintf(file, "\n");
11682
11683     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11684   }
11685
11686   for (i = 0; i < 2; i++)
11687   {
11688     int grid_xsize = setup.touch.grid_xsize[i];
11689     int grid_ysize = setup.touch.grid_ysize[i];
11690     int x, y;
11691
11692     fprintf(file, "\n");
11693
11694     for (y = 0; y < grid_ysize; y++)
11695     {
11696       char token_string[MAX_LINE_LEN];
11697       char value_string[MAX_LINE_LEN];
11698
11699       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11700
11701       for (x = 0; x < grid_xsize; x++)
11702       {
11703         char c = setup.touch.grid_button[i][x][y];
11704
11705         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11706       }
11707
11708       value_string[grid_xsize] = '\0';
11709
11710       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11711     }
11712   }
11713
11714   fprintf(file, "\n");
11715   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11716     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11717
11718   fprintf(file, "\n");
11719   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11720     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11721
11722   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11723   {
11724     char prefix[30];
11725
11726     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11727     fprintf(file, "\n");
11728
11729     setup_input = setup.input[pnr];
11730     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11731       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11732   }
11733
11734   fprintf(file, "\n");
11735   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11736     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11737
11738   // (internal setup values not saved to user setup file)
11739
11740   fprintf(file, "\n");
11741   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11742     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11743         setup.debug.xsn_mode != AUTO)
11744       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11745
11746   fprintf(file, "\n");
11747   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11748     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11749
11750   fclose(file);
11751
11752   SetFilePermissions(filename, PERMS_PRIVATE);
11753 }
11754
11755 void SaveSetup_AutoSetup(void)
11756 {
11757   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11758   FILE *file;
11759   int i;
11760
11761   InitUserDataDirectory();
11762
11763   if (!(file = fopen(filename, MODE_WRITE)))
11764   {
11765     Warn("cannot write auto setup file '%s'", filename);
11766
11767     free(filename);
11768
11769     return;
11770   }
11771
11772   fprintFileHeader(file, AUTOSETUP_FILENAME);
11773
11774   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11775     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11776
11777   fclose(file);
11778
11779   SetFilePermissions(filename, PERMS_PRIVATE);
11780
11781   free(filename);
11782 }
11783
11784 void SaveSetup_ServerSetup(void)
11785 {
11786   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11787   FILE *file;
11788   int i;
11789
11790   InitUserDataDirectory();
11791
11792   if (!(file = fopen(filename, MODE_WRITE)))
11793   {
11794     Warn("cannot write server setup file '%s'", filename);
11795
11796     free(filename);
11797
11798     return;
11799   }
11800
11801   fprintFileHeader(file, SERVERSETUP_FILENAME);
11802
11803   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11804   {
11805     // just to make things nicer :)
11806     if (server_setup_tokens[i].value == &setup.use_api_server)
11807       fprintf(file, "\n");
11808
11809     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11810   }
11811
11812   fclose(file);
11813
11814   SetFilePermissions(filename, PERMS_PRIVATE);
11815
11816   free(filename);
11817 }
11818
11819 void SaveSetup_EditorCascade(void)
11820 {
11821   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11822   FILE *file;
11823   int i;
11824
11825   InitUserDataDirectory();
11826
11827   if (!(file = fopen(filename, MODE_WRITE)))
11828   {
11829     Warn("cannot write editor cascade state file '%s'", filename);
11830
11831     free(filename);
11832
11833     return;
11834   }
11835
11836   fprintFileHeader(file, EDITORCASCADE_FILENAME);
11837
11838   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11839     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11840
11841   fclose(file);
11842
11843   SetFilePermissions(filename, PERMS_PRIVATE);
11844
11845   free(filename);
11846 }
11847
11848 void SaveSetup(void)
11849 {
11850   SaveSetup_Default();
11851   SaveSetup_AutoSetup();
11852   SaveSetup_ServerSetup();
11853   SaveSetup_EditorCascade();
11854 }
11855
11856 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11857                                                   char *filename)
11858 {
11859   FILE *file;
11860
11861   if (!(file = fopen(filename, MODE_WRITE)))
11862   {
11863     Warn("cannot write game controller mappings file '%s'", filename);
11864
11865     return;
11866   }
11867
11868   BEGIN_HASH_ITERATION(mappings_hash, itr)
11869   {
11870     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11871   }
11872   END_HASH_ITERATION(mappings_hash, itr)
11873
11874   fclose(file);
11875 }
11876
11877 void SaveSetup_AddGameControllerMapping(char *mapping)
11878 {
11879   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11880   SetupFileHash *mappings_hash = newSetupFileHash();
11881
11882   InitUserDataDirectory();
11883
11884   // load existing personal game controller mappings
11885   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11886
11887   // add new mapping to personal game controller mappings
11888   addGameControllerMappingToHash(mappings_hash, mapping);
11889
11890   // save updated personal game controller mappings
11891   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11892
11893   freeSetupFileHash(mappings_hash);
11894   free(filename);
11895 }
11896
11897 void LoadCustomElementDescriptions(void)
11898 {
11899   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11900   SetupFileHash *setup_file_hash;
11901   int i;
11902
11903   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11904   {
11905     if (element_info[i].custom_description != NULL)
11906     {
11907       free(element_info[i].custom_description);
11908       element_info[i].custom_description = NULL;
11909     }
11910   }
11911
11912   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11913     return;
11914
11915   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11916   {
11917     char *token = getStringCat2(element_info[i].token_name, ".name");
11918     char *value = getHashEntry(setup_file_hash, token);
11919
11920     if (value != NULL)
11921       element_info[i].custom_description = getStringCopy(value);
11922
11923     free(token);
11924   }
11925
11926   freeSetupFileHash(setup_file_hash);
11927 }
11928
11929 static int getElementFromToken(char *token)
11930 {
11931   char *value = getHashEntry(element_token_hash, token);
11932
11933   if (value != NULL)
11934     return atoi(value);
11935
11936   Warn("unknown element token '%s'", token);
11937
11938   return EL_UNDEFINED;
11939 }
11940
11941 void FreeGlobalAnimEventInfo(void)
11942 {
11943   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11944
11945   if (gaei->event_list == NULL)
11946     return;
11947
11948   int i;
11949
11950   for (i = 0; i < gaei->num_event_lists; i++)
11951   {
11952     checked_free(gaei->event_list[i]->event_value);
11953     checked_free(gaei->event_list[i]);
11954   }
11955
11956   checked_free(gaei->event_list);
11957
11958   gaei->event_list = NULL;
11959   gaei->num_event_lists = 0;
11960 }
11961
11962 static int AddGlobalAnimEventList(void)
11963 {
11964   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11965   int list_pos = gaei->num_event_lists++;
11966
11967   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11968                                      sizeof(struct GlobalAnimEventListInfo *));
11969
11970   gaei->event_list[list_pos] =
11971     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11972
11973   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11974
11975   gaeli->event_value = NULL;
11976   gaeli->num_event_values = 0;
11977
11978   return list_pos;
11979 }
11980
11981 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11982 {
11983   // do not add empty global animation events
11984   if (event_value == ANIM_EVENT_NONE)
11985     return list_pos;
11986
11987   // if list position is undefined, create new list
11988   if (list_pos == ANIM_EVENT_UNDEFINED)
11989     list_pos = AddGlobalAnimEventList();
11990
11991   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11992   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11993   int value_pos = gaeli->num_event_values++;
11994
11995   gaeli->event_value = checked_realloc(gaeli->event_value,
11996                                        gaeli->num_event_values * sizeof(int *));
11997
11998   gaeli->event_value[value_pos] = event_value;
11999
12000   return list_pos;
12001 }
12002
12003 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12004 {
12005   if (list_pos == ANIM_EVENT_UNDEFINED)
12006     return ANIM_EVENT_NONE;
12007
12008   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12009   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12010
12011   return gaeli->event_value[value_pos];
12012 }
12013
12014 int GetGlobalAnimEventValueCount(int list_pos)
12015 {
12016   if (list_pos == ANIM_EVENT_UNDEFINED)
12017     return 0;
12018
12019   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12020   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12021
12022   return gaeli->num_event_values;
12023 }
12024
12025 // This function checks if a string <s> of the format "string1, string2, ..."
12026 // exactly contains a string <s_contained>.
12027
12028 static boolean string_has_parameter(char *s, char *s_contained)
12029 {
12030   char *substring;
12031
12032   if (s == NULL || s_contained == NULL)
12033     return FALSE;
12034
12035   if (strlen(s_contained) > strlen(s))
12036     return FALSE;
12037
12038   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12039   {
12040     char next_char = s[strlen(s_contained)];
12041
12042     // check if next character is delimiter or whitespace
12043     if (next_char == ',' || next_char == '\0' ||
12044         next_char == ' ' || next_char == '\t')
12045       return TRUE;
12046   }
12047
12048   // check if string contains another parameter string after a comma
12049   substring = strchr(s, ',');
12050   if (substring == NULL)        // string does not contain a comma
12051     return FALSE;
12052
12053   // advance string pointer to next character after the comma
12054   substring++;
12055
12056   // skip potential whitespaces after the comma
12057   while (*substring == ' ' || *substring == '\t')
12058     substring++;
12059
12060   return string_has_parameter(substring, s_contained);
12061 }
12062
12063 static int get_anim_parameter_value_ce(char *s)
12064 {
12065   char *s_ptr = s;
12066   char *pattern_1 = "ce_change:custom_";
12067   char *pattern_2 = ".page_";
12068   int pattern_1_len = strlen(pattern_1);
12069   char *matching_char = strstr(s_ptr, pattern_1);
12070   int result = ANIM_EVENT_NONE;
12071
12072   if (matching_char == NULL)
12073     return ANIM_EVENT_NONE;
12074
12075   result = ANIM_EVENT_CE_CHANGE;
12076
12077   s_ptr = matching_char + pattern_1_len;
12078
12079   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12080   if (*s_ptr >= '0' && *s_ptr <= '9')
12081   {
12082     int gic_ce_nr = (*s_ptr++ - '0');
12083
12084     if (*s_ptr >= '0' && *s_ptr <= '9')
12085     {
12086       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12087
12088       if (*s_ptr >= '0' && *s_ptr <= '9')
12089         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12090     }
12091
12092     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12093       return ANIM_EVENT_NONE;
12094
12095     // custom element stored as 0 to 255
12096     gic_ce_nr--;
12097
12098     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12099   }
12100   else
12101   {
12102     // invalid custom element number specified
12103
12104     return ANIM_EVENT_NONE;
12105   }
12106
12107   // check for change page number ("page_X" or "page_XX") (optional)
12108   if (strPrefix(s_ptr, pattern_2))
12109   {
12110     s_ptr += strlen(pattern_2);
12111
12112     if (*s_ptr >= '0' && *s_ptr <= '9')
12113     {
12114       int gic_page_nr = (*s_ptr++ - '0');
12115
12116       if (*s_ptr >= '0' && *s_ptr <= '9')
12117         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12118
12119       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12120         return ANIM_EVENT_NONE;
12121
12122       // change page stored as 1 to 32 (0 means "all change pages")
12123
12124       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12125     }
12126     else
12127     {
12128       // invalid animation part number specified
12129
12130       return ANIM_EVENT_NONE;
12131     }
12132   }
12133
12134   // discard result if next character is neither delimiter nor whitespace
12135   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12136         *s_ptr == ' ' || *s_ptr == '\t'))
12137     return ANIM_EVENT_NONE;
12138
12139   return result;
12140 }
12141
12142 static int get_anim_parameter_value(char *s)
12143 {
12144   int event_value[] =
12145   {
12146     ANIM_EVENT_CLICK,
12147     ANIM_EVENT_INIT,
12148     ANIM_EVENT_START,
12149     ANIM_EVENT_END,
12150     ANIM_EVENT_POST
12151   };
12152   char *pattern_1[] =
12153   {
12154     "click:anim_",
12155     "init:anim_",
12156     "start:anim_",
12157     "end:anim_",
12158     "post:anim_"
12159   };
12160   char *pattern_2 = ".part_";
12161   char *matching_char = NULL;
12162   char *s_ptr = s;
12163   int pattern_1_len = 0;
12164   int result = ANIM_EVENT_NONE;
12165   int i;
12166
12167   result = get_anim_parameter_value_ce(s);
12168
12169   if (result != ANIM_EVENT_NONE)
12170     return result;
12171
12172   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12173   {
12174     matching_char = strstr(s_ptr, pattern_1[i]);
12175     pattern_1_len = strlen(pattern_1[i]);
12176     result = event_value[i];
12177
12178     if (matching_char != NULL)
12179       break;
12180   }
12181
12182   if (matching_char == NULL)
12183     return ANIM_EVENT_NONE;
12184
12185   s_ptr = matching_char + pattern_1_len;
12186
12187   // check for main animation number ("anim_X" or "anim_XX")
12188   if (*s_ptr >= '0' && *s_ptr <= '9')
12189   {
12190     int gic_anim_nr = (*s_ptr++ - '0');
12191
12192     if (*s_ptr >= '0' && *s_ptr <= '9')
12193       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12194
12195     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12196       return ANIM_EVENT_NONE;
12197
12198     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12199   }
12200   else
12201   {
12202     // invalid main animation number specified
12203
12204     return ANIM_EVENT_NONE;
12205   }
12206
12207   // check for animation part number ("part_X" or "part_XX") (optional)
12208   if (strPrefix(s_ptr, pattern_2))
12209   {
12210     s_ptr += strlen(pattern_2);
12211
12212     if (*s_ptr >= '0' && *s_ptr <= '9')
12213     {
12214       int gic_part_nr = (*s_ptr++ - '0');
12215
12216       if (*s_ptr >= '0' && *s_ptr <= '9')
12217         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12218
12219       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12220         return ANIM_EVENT_NONE;
12221
12222       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12223     }
12224     else
12225     {
12226       // invalid animation part number specified
12227
12228       return ANIM_EVENT_NONE;
12229     }
12230   }
12231
12232   // discard result if next character is neither delimiter nor whitespace
12233   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12234         *s_ptr == ' ' || *s_ptr == '\t'))
12235     return ANIM_EVENT_NONE;
12236
12237   return result;
12238 }
12239
12240 static int get_anim_parameter_values(char *s)
12241 {
12242   int list_pos = ANIM_EVENT_UNDEFINED;
12243   int event_value = ANIM_EVENT_DEFAULT;
12244
12245   if (string_has_parameter(s, "any"))
12246     event_value |= ANIM_EVENT_ANY;
12247
12248   if (string_has_parameter(s, "click:self") ||
12249       string_has_parameter(s, "click") ||
12250       string_has_parameter(s, "self"))
12251     event_value |= ANIM_EVENT_SELF;
12252
12253   if (string_has_parameter(s, "unclick:any"))
12254     event_value |= ANIM_EVENT_UNCLICK_ANY;
12255
12256   // if animation event found, add it to global animation event list
12257   if (event_value != ANIM_EVENT_NONE)
12258     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12259
12260   while (s != NULL)
12261   {
12262     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12263     event_value = get_anim_parameter_value(s);
12264
12265     // if animation event found, add it to global animation event list
12266     if (event_value != ANIM_EVENT_NONE)
12267       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12268
12269     // continue with next part of the string, starting with next comma
12270     s = strchr(s + 1, ',');
12271   }
12272
12273   return list_pos;
12274 }
12275
12276 static int get_anim_action_parameter_value(char *token)
12277 {
12278   // check most common default case first to massively speed things up
12279   if (strEqual(token, ARG_UNDEFINED))
12280     return ANIM_EVENT_ACTION_NONE;
12281
12282   int result = getImageIDFromToken(token);
12283
12284   if (result == -1)
12285   {
12286     char *gfx_token = getStringCat2("gfx.", token);
12287
12288     result = getImageIDFromToken(gfx_token);
12289
12290     checked_free(gfx_token);
12291   }
12292
12293   if (result == -1)
12294   {
12295     Key key = getKeyFromX11KeyName(token);
12296
12297     if (key != KSYM_UNDEFINED)
12298       result = -(int)key;
12299   }
12300
12301   if (result == -1)
12302   {
12303     if (isURL(token))
12304     {
12305       result = get_hash_from_string(token);     // unsigned int => int
12306       result = ABS(result);                     // may be negative now
12307       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12308
12309       setHashEntry(anim_url_hash, int2str(result, 0), token);
12310     }
12311   }
12312
12313   if (result == -1)
12314     result = ANIM_EVENT_ACTION_NONE;
12315
12316   return result;
12317 }
12318
12319 int get_parameter_value(char *value_raw, char *suffix, int type)
12320 {
12321   char *value = getStringToLower(value_raw);
12322   int result = 0;       // probably a save default value
12323
12324   if (strEqual(suffix, ".direction"))
12325   {
12326     result = (strEqual(value, "left")  ? MV_LEFT :
12327               strEqual(value, "right") ? MV_RIGHT :
12328               strEqual(value, "up")    ? MV_UP :
12329               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12330   }
12331   else if (strEqual(suffix, ".position"))
12332   {
12333     result = (strEqual(value, "left")   ? POS_LEFT :
12334               strEqual(value, "right")  ? POS_RIGHT :
12335               strEqual(value, "top")    ? POS_TOP :
12336               strEqual(value, "upper")  ? POS_UPPER :
12337               strEqual(value, "middle") ? POS_MIDDLE :
12338               strEqual(value, "lower")  ? POS_LOWER :
12339               strEqual(value, "bottom") ? POS_BOTTOM :
12340               strEqual(value, "any")    ? POS_ANY :
12341               strEqual(value, "ce")     ? POS_CE :
12342               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12343               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12344   }
12345   else if (strEqual(suffix, ".align"))
12346   {
12347     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12348               strEqual(value, "right")  ? ALIGN_RIGHT :
12349               strEqual(value, "center") ? ALIGN_CENTER :
12350               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12351   }
12352   else if (strEqual(suffix, ".valign"))
12353   {
12354     result = (strEqual(value, "top")    ? VALIGN_TOP :
12355               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12356               strEqual(value, "middle") ? VALIGN_MIDDLE :
12357               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12358   }
12359   else if (strEqual(suffix, ".anim_mode"))
12360   {
12361     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12362               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12363               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12364               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12365               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12366               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12367               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12368               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12369               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12370               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12371               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12372               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12373               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12374               string_has_parameter(value, "all")        ? ANIM_ALL :
12375               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12376               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12377               ANIM_DEFAULT);
12378
12379     if (string_has_parameter(value, "once"))
12380       result |= ANIM_ONCE;
12381
12382     if (string_has_parameter(value, "reverse"))
12383       result |= ANIM_REVERSE;
12384
12385     if (string_has_parameter(value, "opaque_player"))
12386       result |= ANIM_OPAQUE_PLAYER;
12387
12388     if (string_has_parameter(value, "static_panel"))
12389       result |= ANIM_STATIC_PANEL;
12390   }
12391   else if (strEqual(suffix, ".init_event") ||
12392            strEqual(suffix, ".anim_event"))
12393   {
12394     result = get_anim_parameter_values(value);
12395   }
12396   else if (strEqual(suffix, ".init_delay_action") ||
12397            strEqual(suffix, ".anim_delay_action") ||
12398            strEqual(suffix, ".post_delay_action") ||
12399            strEqual(suffix, ".init_event_action") ||
12400            strEqual(suffix, ".anim_event_action"))
12401   {
12402     result = get_anim_action_parameter_value(value_raw);
12403   }
12404   else if (strEqual(suffix, ".class"))
12405   {
12406     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12407               get_hash_from_string(value));
12408   }
12409   else if (strEqual(suffix, ".style"))
12410   {
12411     result = STYLE_DEFAULT;
12412
12413     if (string_has_parameter(value, "accurate_borders"))
12414       result |= STYLE_ACCURATE_BORDERS;
12415
12416     if (string_has_parameter(value, "inner_corners"))
12417       result |= STYLE_INNER_CORNERS;
12418
12419     if (string_has_parameter(value, "reverse"))
12420       result |= STYLE_REVERSE;
12421
12422     if (string_has_parameter(value, "leftmost_position"))
12423       result |= STYLE_LEFTMOST_POSITION;
12424
12425     if (string_has_parameter(value, "block_clicks"))
12426       result |= STYLE_BLOCK;
12427
12428     if (string_has_parameter(value, "passthrough_clicks"))
12429       result |= STYLE_PASSTHROUGH;
12430
12431     if (string_has_parameter(value, "multiple_actions"))
12432       result |= STYLE_MULTIPLE_ACTIONS;
12433
12434     if (string_has_parameter(value, "consume_ce_event"))
12435       result |= STYLE_CONSUME_CE_EVENT;
12436   }
12437   else if (strEqual(suffix, ".fade_mode"))
12438   {
12439     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12440               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12441               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12442               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12443               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12444               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12445               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12446               FADE_MODE_DEFAULT);
12447   }
12448   else if (strEqual(suffix, ".auto_delay_unit"))
12449   {
12450     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12451               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12452               AUTO_DELAY_UNIT_DEFAULT);
12453   }
12454   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12455   {
12456     result = gfx.get_font_from_token_function(value);
12457   }
12458   else          // generic parameter of type integer or boolean
12459   {
12460     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12461               type == TYPE_INTEGER ? get_integer_from_string(value) :
12462               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12463               ARG_UNDEFINED_VALUE);
12464   }
12465
12466   free(value);
12467
12468   return result;
12469 }
12470
12471 static int get_token_parameter_value(char *token, char *value_raw)
12472 {
12473   char *suffix;
12474
12475   if (token == NULL || value_raw == NULL)
12476     return ARG_UNDEFINED_VALUE;
12477
12478   suffix = strrchr(token, '.');
12479   if (suffix == NULL)
12480     suffix = token;
12481
12482   if (strEqual(suffix, ".element"))
12483     return getElementFromToken(value_raw);
12484
12485   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12486   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12487 }
12488
12489 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12490                                      boolean ignore_defaults)
12491 {
12492   int i;
12493
12494   for (i = 0; image_config_vars[i].token != NULL; i++)
12495   {
12496     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12497
12498     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12499     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12500       continue;
12501
12502     if (value != NULL)
12503       *image_config_vars[i].value =
12504         get_token_parameter_value(image_config_vars[i].token, value);
12505   }
12506 }
12507
12508 void InitMenuDesignSettings_Static(void)
12509 {
12510   // always start with reliable default values from static default config
12511   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12512 }
12513
12514 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12515 {
12516   int i;
12517
12518   // the following initializes hierarchical values from static configuration
12519
12520   // special case: initialize "ARG_DEFAULT" values in static default config
12521   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12522   titlescreen_initial_first_default.fade_mode  =
12523     title_initial_first_default.fade_mode;
12524   titlescreen_initial_first_default.fade_delay =
12525     title_initial_first_default.fade_delay;
12526   titlescreen_initial_first_default.post_delay =
12527     title_initial_first_default.post_delay;
12528   titlescreen_initial_first_default.auto_delay =
12529     title_initial_first_default.auto_delay;
12530   titlescreen_initial_first_default.auto_delay_unit =
12531     title_initial_first_default.auto_delay_unit;
12532   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12533   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12534   titlescreen_first_default.post_delay = title_first_default.post_delay;
12535   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12536   titlescreen_first_default.auto_delay_unit =
12537     title_first_default.auto_delay_unit;
12538   titlemessage_initial_first_default.fade_mode  =
12539     title_initial_first_default.fade_mode;
12540   titlemessage_initial_first_default.fade_delay =
12541     title_initial_first_default.fade_delay;
12542   titlemessage_initial_first_default.post_delay =
12543     title_initial_first_default.post_delay;
12544   titlemessage_initial_first_default.auto_delay =
12545     title_initial_first_default.auto_delay;
12546   titlemessage_initial_first_default.auto_delay_unit =
12547     title_initial_first_default.auto_delay_unit;
12548   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12549   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12550   titlemessage_first_default.post_delay = title_first_default.post_delay;
12551   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12552   titlemessage_first_default.auto_delay_unit =
12553     title_first_default.auto_delay_unit;
12554
12555   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12556   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12557   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12558   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12559   titlescreen_initial_default.auto_delay_unit =
12560     title_initial_default.auto_delay_unit;
12561   titlescreen_default.fade_mode  = title_default.fade_mode;
12562   titlescreen_default.fade_delay = title_default.fade_delay;
12563   titlescreen_default.post_delay = title_default.post_delay;
12564   titlescreen_default.auto_delay = title_default.auto_delay;
12565   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12566   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12567   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12568   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12569   titlemessage_initial_default.auto_delay_unit =
12570     title_initial_default.auto_delay_unit;
12571   titlemessage_default.fade_mode  = title_default.fade_mode;
12572   titlemessage_default.fade_delay = title_default.fade_delay;
12573   titlemessage_default.post_delay = title_default.post_delay;
12574   titlemessage_default.auto_delay = title_default.auto_delay;
12575   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12576
12577   // special case: initialize "ARG_DEFAULT" values in static default config
12578   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12579   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12580   {
12581     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12582     titlescreen_first[i] = titlescreen_first_default;
12583     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12584     titlemessage_first[i] = titlemessage_first_default;
12585
12586     titlescreen_initial[i] = titlescreen_initial_default;
12587     titlescreen[i] = titlescreen_default;
12588     titlemessage_initial[i] = titlemessage_initial_default;
12589     titlemessage[i] = titlemessage_default;
12590   }
12591
12592   // special case: initialize "ARG_DEFAULT" values in static default config
12593   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12594   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12595   {
12596     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12597       continue;
12598
12599     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12600     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12601     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12602   }
12603
12604   // special case: initialize "ARG_DEFAULT" values in static default config
12605   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12606   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12607   {
12608     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12609     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12610     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12611
12612     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12613       continue;
12614
12615     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12616   }
12617 }
12618
12619 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12620 {
12621   static struct
12622   {
12623     struct XY *dst, *src;
12624   }
12625   game_buttons_xy[] =
12626   {
12627     { &game.button.save,        &game.button.stop       },
12628     { &game.button.pause2,      &game.button.pause      },
12629     { &game.button.load,        &game.button.play       },
12630     { &game.button.undo,        &game.button.stop       },
12631     { &game.button.redo,        &game.button.play       },
12632
12633     { NULL,                     NULL                    }
12634   };
12635   int i, j;
12636
12637   // special case: initialize later added SETUP list size from LEVELS value
12638   if (menu.list_size[GAME_MODE_SETUP] == -1)
12639     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12640
12641   // set default position for snapshot buttons to stop/pause/play buttons
12642   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12643     if ((*game_buttons_xy[i].dst).x == -1 &&
12644         (*game_buttons_xy[i].dst).y == -1)
12645       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12646
12647   // --------------------------------------------------------------------------
12648   // dynamic viewports (including playfield margins, borders and alignments)
12649   // --------------------------------------------------------------------------
12650
12651   // dynamic viewports currently only supported for landscape mode
12652   int display_width  = MAX(video.display_width, video.display_height);
12653   int display_height = MIN(video.display_width, video.display_height);
12654
12655   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12656   {
12657     struct RectWithBorder *vp_window    = &viewport.window[i];
12658     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12659     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12660     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12661     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12662     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12663     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12664     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12665
12666     // adjust window size if min/max width/height is specified
12667
12668     if (vp_window->min_width != -1)
12669     {
12670       int window_width = display_width;
12671
12672       // when using static window height, use aspect ratio of display
12673       if (vp_window->min_height == -1)
12674         window_width = vp_window->height * display_width / display_height;
12675
12676       vp_window->width = MAX(vp_window->min_width, window_width);
12677     }
12678
12679     if (vp_window->min_height != -1)
12680     {
12681       int window_height = display_height;
12682
12683       // when using static window width, use aspect ratio of display
12684       if (vp_window->min_width == -1)
12685         window_height = vp_window->width * display_height / display_width;
12686
12687       vp_window->height = MAX(vp_window->min_height, window_height);
12688     }
12689
12690     if (vp_window->max_width != -1)
12691       vp_window->width = MIN(vp_window->width, vp_window->max_width);
12692
12693     if (vp_window->max_height != -1)
12694       vp_window->height = MIN(vp_window->height, vp_window->max_height);
12695
12696     int playfield_width  = vp_window->width;
12697     int playfield_height = vp_window->height;
12698
12699     // adjust playfield size and position according to specified margins
12700
12701     playfield_width  -= vp_playfield->margin_left;
12702     playfield_width  -= vp_playfield->margin_right;
12703
12704     playfield_height -= vp_playfield->margin_top;
12705     playfield_height -= vp_playfield->margin_bottom;
12706
12707     // adjust playfield size if min/max width/height is specified
12708
12709     if (vp_playfield->min_width != -1)
12710       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12711
12712     if (vp_playfield->min_height != -1)
12713       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12714
12715     if (vp_playfield->max_width != -1)
12716       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12717
12718     if (vp_playfield->max_height != -1)
12719       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12720
12721     // adjust playfield position according to specified alignment
12722
12723     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12724       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12725     else if (vp_playfield->align == ALIGN_CENTER)
12726       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12727     else if (vp_playfield->align == ALIGN_RIGHT)
12728       vp_playfield->x += playfield_width - vp_playfield->width;
12729
12730     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12731       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12732     else if (vp_playfield->valign == VALIGN_MIDDLE)
12733       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12734     else if (vp_playfield->valign == VALIGN_BOTTOM)
12735       vp_playfield->y += playfield_height - vp_playfield->height;
12736
12737     vp_playfield->x += vp_playfield->margin_left;
12738     vp_playfield->y += vp_playfield->margin_top;
12739
12740     // adjust individual playfield borders if only default border is specified
12741
12742     if (vp_playfield->border_left == -1)
12743       vp_playfield->border_left = vp_playfield->border_size;
12744     if (vp_playfield->border_right == -1)
12745       vp_playfield->border_right = vp_playfield->border_size;
12746     if (vp_playfield->border_top == -1)
12747       vp_playfield->border_top = vp_playfield->border_size;
12748     if (vp_playfield->border_bottom == -1)
12749       vp_playfield->border_bottom = vp_playfield->border_size;
12750
12751     // set dynamic playfield borders if borders are specified as undefined
12752     // (but only if window size was dynamic and playfield size was static)
12753
12754     if (dynamic_window_width && !dynamic_playfield_width)
12755     {
12756       if (vp_playfield->border_left == -1)
12757       {
12758         vp_playfield->border_left = (vp_playfield->x -
12759                                      vp_playfield->margin_left);
12760         vp_playfield->x     -= vp_playfield->border_left;
12761         vp_playfield->width += vp_playfield->border_left;
12762       }
12763
12764       if (vp_playfield->border_right == -1)
12765       {
12766         vp_playfield->border_right = (vp_window->width -
12767                                       vp_playfield->x -
12768                                       vp_playfield->width -
12769                                       vp_playfield->margin_right);
12770         vp_playfield->width += vp_playfield->border_right;
12771       }
12772     }
12773
12774     if (dynamic_window_height && !dynamic_playfield_height)
12775     {
12776       if (vp_playfield->border_top == -1)
12777       {
12778         vp_playfield->border_top = (vp_playfield->y -
12779                                     vp_playfield->margin_top);
12780         vp_playfield->y      -= vp_playfield->border_top;
12781         vp_playfield->height += vp_playfield->border_top;
12782       }
12783
12784       if (vp_playfield->border_bottom == -1)
12785       {
12786         vp_playfield->border_bottom = (vp_window->height -
12787                                        vp_playfield->y -
12788                                        vp_playfield->height -
12789                                        vp_playfield->margin_bottom);
12790         vp_playfield->height += vp_playfield->border_bottom;
12791       }
12792     }
12793
12794     // adjust playfield size to be a multiple of a defined alignment tile size
12795
12796     int align_size = vp_playfield->align_size;
12797     int playfield_xtiles = vp_playfield->width  / align_size;
12798     int playfield_ytiles = vp_playfield->height / align_size;
12799     int playfield_width_corrected  = playfield_xtiles * align_size;
12800     int playfield_height_corrected = playfield_ytiles * align_size;
12801     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12802                                  i == GFX_SPECIAL_ARG_EDITOR);
12803
12804     if (is_playfield_mode &&
12805         dynamic_playfield_width &&
12806         vp_playfield->width != playfield_width_corrected)
12807     {
12808       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12809
12810       vp_playfield->width = playfield_width_corrected;
12811
12812       if (vp_playfield->align == ALIGN_LEFT)
12813       {
12814         vp_playfield->border_left += playfield_xdiff;
12815       }
12816       else if (vp_playfield->align == ALIGN_RIGHT)
12817       {
12818         vp_playfield->border_right += playfield_xdiff;
12819       }
12820       else if (vp_playfield->align == ALIGN_CENTER)
12821       {
12822         int border_left_diff  = playfield_xdiff / 2;
12823         int border_right_diff = playfield_xdiff - border_left_diff;
12824
12825         vp_playfield->border_left  += border_left_diff;
12826         vp_playfield->border_right += border_right_diff;
12827       }
12828     }
12829
12830     if (is_playfield_mode &&
12831         dynamic_playfield_height &&
12832         vp_playfield->height != playfield_height_corrected)
12833     {
12834       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12835
12836       vp_playfield->height = playfield_height_corrected;
12837
12838       if (vp_playfield->valign == VALIGN_TOP)
12839       {
12840         vp_playfield->border_top += playfield_ydiff;
12841       }
12842       else if (vp_playfield->align == VALIGN_BOTTOM)
12843       {
12844         vp_playfield->border_right += playfield_ydiff;
12845       }
12846       else if (vp_playfield->align == VALIGN_MIDDLE)
12847       {
12848         int border_top_diff    = playfield_ydiff / 2;
12849         int border_bottom_diff = playfield_ydiff - border_top_diff;
12850
12851         vp_playfield->border_top    += border_top_diff;
12852         vp_playfield->border_bottom += border_bottom_diff;
12853       }
12854     }
12855
12856     // adjust door positions according to specified alignment
12857
12858     for (j = 0; j < 2; j++)
12859     {
12860       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12861
12862       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12863         vp_door->x = ALIGNED_VP_XPOS(vp_door);
12864       else if (vp_door->align == ALIGN_CENTER)
12865         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12866       else if (vp_door->align == ALIGN_RIGHT)
12867         vp_door->x += vp_window->width - vp_door->width;
12868
12869       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12870         vp_door->y = ALIGNED_VP_YPOS(vp_door);
12871       else if (vp_door->valign == VALIGN_MIDDLE)
12872         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12873       else if (vp_door->valign == VALIGN_BOTTOM)
12874         vp_door->y += vp_window->height - vp_door->height;
12875     }
12876   }
12877 }
12878
12879 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12880 {
12881   static struct
12882   {
12883     struct XYTileSize *dst, *src;
12884     int graphic;
12885   }
12886   editor_buttons_xy[] =
12887   {
12888     {
12889       &editor.button.element_left,      &editor.palette.element_left,
12890       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12891     },
12892     {
12893       &editor.button.element_middle,    &editor.palette.element_middle,
12894       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12895     },
12896     {
12897       &editor.button.element_right,     &editor.palette.element_right,
12898       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12899     },
12900
12901     { NULL,                     NULL                    }
12902   };
12903   int i;
12904
12905   // set default position for element buttons to element graphics
12906   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12907   {
12908     if ((*editor_buttons_xy[i].dst).x == -1 &&
12909         (*editor_buttons_xy[i].dst).y == -1)
12910     {
12911       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12912
12913       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12914
12915       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12916     }
12917   }
12918
12919   // adjust editor palette rows and columns if specified to be dynamic
12920
12921   if (editor.palette.cols == -1)
12922   {
12923     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12924     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12925     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12926
12927     editor.palette.cols = (vp_width - sc_width) / bt_width;
12928
12929     if (editor.palette.x == -1)
12930     {
12931       int palette_width = editor.palette.cols * bt_width + sc_width;
12932
12933       editor.palette.x = (vp_width - palette_width) / 2;
12934     }
12935   }
12936
12937   if (editor.palette.rows == -1)
12938   {
12939     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12940     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12941     int tx_height = getFontHeight(FONT_TEXT_2);
12942
12943     editor.palette.rows = (vp_height - tx_height) / bt_height;
12944
12945     if (editor.palette.y == -1)
12946     {
12947       int palette_height = editor.palette.rows * bt_height + tx_height;
12948
12949       editor.palette.y = (vp_height - palette_height) / 2;
12950     }
12951   }
12952 }
12953
12954 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12955                                                       boolean initialize)
12956 {
12957   // special case: check if network and preview player positions are redefined,
12958   // to compare this later against the main menu level preview being redefined
12959   struct TokenIntPtrInfo menu_config_players[] =
12960   {
12961     { "main.network_players.x", &menu.main.network_players.redefined    },
12962     { "main.network_players.y", &menu.main.network_players.redefined    },
12963     { "main.preview_players.x", &menu.main.preview_players.redefined    },
12964     { "main.preview_players.y", &menu.main.preview_players.redefined    },
12965     { "preview.x",              &preview.redefined                      },
12966     { "preview.y",              &preview.redefined                      }
12967   };
12968   int i;
12969
12970   if (initialize)
12971   {
12972     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12973       *menu_config_players[i].value = FALSE;
12974   }
12975   else
12976   {
12977     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12978       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12979         *menu_config_players[i].value = TRUE;
12980   }
12981 }
12982
12983 static void InitMenuDesignSettings_PreviewPlayers(void)
12984 {
12985   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12986 }
12987
12988 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12989 {
12990   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12991 }
12992
12993 static void LoadMenuDesignSettingsFromFilename(char *filename)
12994 {
12995   static struct TitleFadingInfo tfi;
12996   static struct TitleMessageInfo tmi;
12997   static struct TokenInfo title_tokens[] =
12998   {
12999     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13000     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13001     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13002     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13003     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13004
13005     { -1,               NULL,                   NULL                    }
13006   };
13007   static struct TokenInfo titlemessage_tokens[] =
13008   {
13009     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13010     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13011     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13012     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13013     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13014     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13015     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13016     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13017     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13018     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13019     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13020     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13021     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13022     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13023     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13024     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13025     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13026     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13027
13028     { -1,               NULL,                   NULL                    }
13029   };
13030   static struct
13031   {
13032     struct TitleFadingInfo *info;
13033     char *text;
13034   }
13035   title_info[] =
13036   {
13037     // initialize first titles from "enter screen" definitions, if defined
13038     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13039     { &title_first_default,             "menu.enter_screen.TITLE"       },
13040
13041     // initialize title screens from "next screen" definitions, if defined
13042     { &title_initial_default,           "menu.next_screen.TITLE"        },
13043     { &title_default,                   "menu.next_screen.TITLE"        },
13044
13045     { NULL,                             NULL                            }
13046   };
13047   static struct
13048   {
13049     struct TitleMessageInfo *array;
13050     char *text;
13051   }
13052   titlemessage_arrays[] =
13053   {
13054     // initialize first titles from "enter screen" definitions, if defined
13055     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13056     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13057     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13058     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13059
13060     // initialize titles from "next screen" definitions, if defined
13061     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13062     { titlescreen,                      "menu.next_screen.TITLE"        },
13063     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13064     { titlemessage,                     "menu.next_screen.TITLE"        },
13065
13066     // overwrite titles with title definitions, if defined
13067     { titlescreen_initial_first,        "[title_initial]"               },
13068     { titlescreen_first,                "[title]"                       },
13069     { titlemessage_initial_first,       "[title_initial]"               },
13070     { titlemessage_first,               "[title]"                       },
13071
13072     { titlescreen_initial,              "[title_initial]"               },
13073     { titlescreen,                      "[title]"                       },
13074     { titlemessage_initial,             "[title_initial]"               },
13075     { titlemessage,                     "[title]"                       },
13076
13077     // overwrite titles with title screen/message definitions, if defined
13078     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13079     { titlescreen_first,                "[titlescreen]"                 },
13080     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13081     { titlemessage_first,               "[titlemessage]"                },
13082
13083     { titlescreen_initial,              "[titlescreen_initial]"         },
13084     { titlescreen,                      "[titlescreen]"                 },
13085     { titlemessage_initial,             "[titlemessage_initial]"        },
13086     { titlemessage,                     "[titlemessage]"                },
13087
13088     { NULL,                             NULL                            }
13089   };
13090   SetupFileHash *setup_file_hash;
13091   int i, j, k;
13092
13093   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13094     return;
13095
13096   // the following initializes hierarchical values from dynamic configuration
13097
13098   // special case: initialize with default values that may be overwritten
13099   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13100   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13101   {
13102     struct TokenIntPtrInfo menu_config[] =
13103     {
13104       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13105       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13106       { "menu.list_size",       &menu.list_size[i]      }
13107     };
13108
13109     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13110     {
13111       char *token = menu_config[j].token;
13112       char *value = getHashEntry(setup_file_hash, token);
13113
13114       if (value != NULL)
13115         *menu_config[j].value = get_integer_from_string(value);
13116     }
13117   }
13118
13119   // special case: initialize with default values that may be overwritten
13120   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13121   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13122   {
13123     struct TokenIntPtrInfo menu_config[] =
13124     {
13125       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13126       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13127       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13128       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13129       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13130     };
13131
13132     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13133     {
13134       char *token = menu_config[j].token;
13135       char *value = getHashEntry(setup_file_hash, token);
13136
13137       if (value != NULL)
13138         *menu_config[j].value = get_integer_from_string(value);
13139     }
13140   }
13141
13142   // special case: initialize with default values that may be overwritten
13143   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13144   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13145   {
13146     struct TokenIntPtrInfo menu_config[] =
13147     {
13148       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13149       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13150     };
13151
13152     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13153     {
13154       char *token = menu_config[j].token;
13155       char *value = getHashEntry(setup_file_hash, token);
13156
13157       if (value != NULL)
13158         *menu_config[j].value = get_integer_from_string(value);
13159     }
13160   }
13161
13162   // special case: initialize with default values that may be overwritten
13163   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13164   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13165   {
13166     struct TokenIntPtrInfo menu_config[] =
13167     {
13168       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13169       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13170       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13171       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13172       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13173       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13174       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13175       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13176       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13177       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13178     };
13179
13180     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13181     {
13182       char *token = menu_config[j].token;
13183       char *value = getHashEntry(setup_file_hash, token);
13184
13185       if (value != NULL)
13186         *menu_config[j].value = get_integer_from_string(value);
13187     }
13188   }
13189
13190   // special case: initialize with default values that may be overwritten
13191   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13192   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13193   {
13194     struct TokenIntPtrInfo menu_config[] =
13195     {
13196       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13197       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13198       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13199       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13200       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13201       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13202       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13203       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13204       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13205     };
13206
13207     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13208     {
13209       char *token = menu_config[j].token;
13210       char *value = getHashEntry(setup_file_hash, token);
13211
13212       if (value != NULL)
13213         *menu_config[j].value = get_token_parameter_value(token, value);
13214     }
13215   }
13216
13217   // special case: initialize with default values that may be overwritten
13218   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13219   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13220   {
13221     struct
13222     {
13223       char *token_prefix;
13224       struct RectWithBorder *struct_ptr;
13225     }
13226     vp_struct[] =
13227     {
13228       { "viewport.window",      &viewport.window[i]     },
13229       { "viewport.playfield",   &viewport.playfield[i]  },
13230       { "viewport.door_1",      &viewport.door_1[i]     },
13231       { "viewport.door_2",      &viewport.door_2[i]     }
13232     };
13233
13234     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13235     {
13236       struct TokenIntPtrInfo vp_config[] =
13237       {
13238         { ".x",                 &vp_struct[j].struct_ptr->x             },
13239         { ".y",                 &vp_struct[j].struct_ptr->y             },
13240         { ".width",             &vp_struct[j].struct_ptr->width         },
13241         { ".height",            &vp_struct[j].struct_ptr->height        },
13242         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13243         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13244         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13245         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13246         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13247         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13248         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13249         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13250         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13251         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13252         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13253         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13254         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13255         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13256         { ".align",             &vp_struct[j].struct_ptr->align         },
13257         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13258       };
13259
13260       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13261       {
13262         char *token = getStringCat2(vp_struct[j].token_prefix,
13263                                     vp_config[k].token);
13264         char *value = getHashEntry(setup_file_hash, token);
13265
13266         if (value != NULL)
13267           *vp_config[k].value = get_token_parameter_value(token, value);
13268
13269         free(token);
13270       }
13271     }
13272   }
13273
13274   // special case: initialize with default values that may be overwritten
13275   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13276   for (i = 0; title_info[i].info != NULL; i++)
13277   {
13278     struct TitleFadingInfo *info = title_info[i].info;
13279     char *base_token = title_info[i].text;
13280
13281     for (j = 0; title_tokens[j].type != -1; j++)
13282     {
13283       char *token = getStringCat2(base_token, title_tokens[j].text);
13284       char *value = getHashEntry(setup_file_hash, token);
13285
13286       if (value != NULL)
13287       {
13288         int parameter_value = get_token_parameter_value(token, value);
13289
13290         tfi = *info;
13291
13292         *(int *)title_tokens[j].value = (int)parameter_value;
13293
13294         *info = tfi;
13295       }
13296
13297       free(token);
13298     }
13299   }
13300
13301   // special case: initialize with default values that may be overwritten
13302   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13303   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13304   {
13305     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13306     char *base_token = titlemessage_arrays[i].text;
13307
13308     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13309     {
13310       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13311       char *value = getHashEntry(setup_file_hash, token);
13312
13313       if (value != NULL)
13314       {
13315         int parameter_value = get_token_parameter_value(token, value);
13316
13317         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13318         {
13319           tmi = array[k];
13320
13321           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13322             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13323           else
13324             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13325
13326           array[k] = tmi;
13327         }
13328       }
13329
13330       free(token);
13331     }
13332   }
13333
13334   // read (and overwrite with) values that may be specified in config file
13335   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13336
13337   // special case: check if network and preview player positions are redefined
13338   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13339
13340   freeSetupFileHash(setup_file_hash);
13341 }
13342
13343 void LoadMenuDesignSettings(void)
13344 {
13345   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13346
13347   InitMenuDesignSettings_Static();
13348   InitMenuDesignSettings_SpecialPreProcessing();
13349   InitMenuDesignSettings_PreviewPlayers();
13350
13351   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13352   {
13353     // first look for special settings configured in level series config
13354     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13355
13356     if (fileExists(filename_base))
13357       LoadMenuDesignSettingsFromFilename(filename_base);
13358   }
13359
13360   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13361
13362   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13363     LoadMenuDesignSettingsFromFilename(filename_local);
13364
13365   InitMenuDesignSettings_SpecialPostProcessing();
13366 }
13367
13368 void LoadMenuDesignSettings_AfterGraphics(void)
13369 {
13370   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13371 }
13372
13373 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13374                                 boolean ignore_defaults)
13375 {
13376   int i;
13377
13378   for (i = 0; sound_config_vars[i].token != NULL; i++)
13379   {
13380     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13381
13382     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13383     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13384       continue;
13385
13386     if (value != NULL)
13387       *sound_config_vars[i].value =
13388         get_token_parameter_value(sound_config_vars[i].token, value);
13389   }
13390 }
13391
13392 void InitSoundSettings_Static(void)
13393 {
13394   // always start with reliable default values from static default config
13395   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13396 }
13397
13398 static void LoadSoundSettingsFromFilename(char *filename)
13399 {
13400   SetupFileHash *setup_file_hash;
13401
13402   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13403     return;
13404
13405   // read (and overwrite with) values that may be specified in config file
13406   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13407
13408   freeSetupFileHash(setup_file_hash);
13409 }
13410
13411 void LoadSoundSettings(void)
13412 {
13413   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13414
13415   InitSoundSettings_Static();
13416
13417   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13418   {
13419     // first look for special settings configured in level series config
13420     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13421
13422     if (fileExists(filename_base))
13423       LoadSoundSettingsFromFilename(filename_base);
13424   }
13425
13426   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13427
13428   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13429     LoadSoundSettingsFromFilename(filename_local);
13430 }
13431
13432 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13433 {
13434   char *filename = getEditorSetupFilename();
13435   SetupFileList *setup_file_list, *list;
13436   SetupFileHash *element_hash;
13437   int num_unknown_tokens = 0;
13438   int i;
13439
13440   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13441     return;
13442
13443   element_hash = newSetupFileHash();
13444
13445   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13446     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13447
13448   // determined size may be larger than needed (due to unknown elements)
13449   *num_elements = 0;
13450   for (list = setup_file_list; list != NULL; list = list->next)
13451     (*num_elements)++;
13452
13453   // add space for up to 3 more elements for padding that may be needed
13454   *num_elements += 3;
13455
13456   // free memory for old list of elements, if needed
13457   checked_free(*elements);
13458
13459   // allocate memory for new list of elements
13460   *elements = checked_malloc(*num_elements * sizeof(int));
13461
13462   *num_elements = 0;
13463   for (list = setup_file_list; list != NULL; list = list->next)
13464   {
13465     char *value = getHashEntry(element_hash, list->token);
13466
13467     if (value == NULL)          // try to find obsolete token mapping
13468     {
13469       char *mapped_token = get_mapped_token(list->token);
13470
13471       if (mapped_token != NULL)
13472       {
13473         value = getHashEntry(element_hash, mapped_token);
13474
13475         free(mapped_token);
13476       }
13477     }
13478
13479     if (value != NULL)
13480     {
13481       (*elements)[(*num_elements)++] = atoi(value);
13482     }
13483     else
13484     {
13485       if (num_unknown_tokens == 0)
13486       {
13487         Warn("---");
13488         Warn("unknown token(s) found in config file:");
13489         Warn("- config file: '%s'", filename);
13490
13491         num_unknown_tokens++;
13492       }
13493
13494       Warn("- token: '%s'", list->token);
13495     }
13496   }
13497
13498   if (num_unknown_tokens > 0)
13499     Warn("---");
13500
13501   while (*num_elements % 4)     // pad with empty elements, if needed
13502     (*elements)[(*num_elements)++] = EL_EMPTY;
13503
13504   freeSetupFileList(setup_file_list);
13505   freeSetupFileHash(element_hash);
13506
13507 #if 0
13508   for (i = 0; i < *num_elements; i++)
13509     Debug("editor", "element '%s' [%d]\n",
13510           element_info[(*elements)[i]].token_name, (*elements)[i]);
13511 #endif
13512 }
13513
13514 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13515                                                      boolean is_sound)
13516 {
13517   SetupFileHash *setup_file_hash = NULL;
13518   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13519   char *filename_music, *filename_prefix, *filename_info;
13520   struct
13521   {
13522     char *token;
13523     char **value_ptr;
13524   }
13525   token_to_value_ptr[] =
13526   {
13527     { "title_header",   &tmp_music_file_info.title_header       },
13528     { "artist_header",  &tmp_music_file_info.artist_header      },
13529     { "album_header",   &tmp_music_file_info.album_header       },
13530     { "year_header",    &tmp_music_file_info.year_header        },
13531     { "played_header",  &tmp_music_file_info.played_header      },
13532
13533     { "title",          &tmp_music_file_info.title              },
13534     { "artist",         &tmp_music_file_info.artist             },
13535     { "album",          &tmp_music_file_info.album              },
13536     { "year",           &tmp_music_file_info.year               },
13537     { "played",         &tmp_music_file_info.played             },
13538
13539     { NULL,             NULL                                    },
13540   };
13541   int i;
13542
13543   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13544                     getCustomMusicFilename(basename));
13545
13546   if (filename_music == NULL)
13547     return NULL;
13548
13549   // ---------- try to replace file extension ----------
13550
13551   filename_prefix = getStringCopy(filename_music);
13552   if (strrchr(filename_prefix, '.') != NULL)
13553     *strrchr(filename_prefix, '.') = '\0';
13554   filename_info = getStringCat2(filename_prefix, ".txt");
13555
13556   if (fileExists(filename_info))
13557     setup_file_hash = loadSetupFileHash(filename_info);
13558
13559   free(filename_prefix);
13560   free(filename_info);
13561
13562   if (setup_file_hash == NULL)
13563   {
13564     // ---------- try to add file extension ----------
13565
13566     filename_prefix = getStringCopy(filename_music);
13567     filename_info = getStringCat2(filename_prefix, ".txt");
13568
13569     if (fileExists(filename_info))
13570       setup_file_hash = loadSetupFileHash(filename_info);
13571
13572     free(filename_prefix);
13573     free(filename_info);
13574   }
13575
13576   if (setup_file_hash == NULL)
13577     return NULL;
13578
13579   // ---------- music file info found ----------
13580
13581   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13582
13583   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13584   {
13585     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13586
13587     *token_to_value_ptr[i].value_ptr =
13588       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13589   }
13590
13591   tmp_music_file_info.basename = getStringCopy(basename);
13592   tmp_music_file_info.music = music;
13593   tmp_music_file_info.is_sound = is_sound;
13594
13595   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13596   *new_music_file_info = tmp_music_file_info;
13597
13598   return new_music_file_info;
13599 }
13600
13601 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13602 {
13603   return get_music_file_info_ext(basename, music, FALSE);
13604 }
13605
13606 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13607 {
13608   return get_music_file_info_ext(basename, sound, TRUE);
13609 }
13610
13611 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13612                                      char *basename, boolean is_sound)
13613 {
13614   for (; list != NULL; list = list->next)
13615     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13616       return TRUE;
13617
13618   return FALSE;
13619 }
13620
13621 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13622 {
13623   return music_info_listed_ext(list, basename, FALSE);
13624 }
13625
13626 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13627 {
13628   return music_info_listed_ext(list, basename, TRUE);
13629 }
13630
13631 void LoadMusicInfo(void)
13632 {
13633   int num_music_noconf = getMusicListSize_NoConf();
13634   int num_music = getMusicListSize();
13635   int num_sounds = getSoundListSize();
13636   struct FileInfo *music, *sound;
13637   struct MusicFileInfo *next, **new;
13638
13639   int i;
13640
13641   while (music_file_info != NULL)
13642   {
13643     next = music_file_info->next;
13644
13645     checked_free(music_file_info->basename);
13646
13647     checked_free(music_file_info->title_header);
13648     checked_free(music_file_info->artist_header);
13649     checked_free(music_file_info->album_header);
13650     checked_free(music_file_info->year_header);
13651     checked_free(music_file_info->played_header);
13652
13653     checked_free(music_file_info->title);
13654     checked_free(music_file_info->artist);
13655     checked_free(music_file_info->album);
13656     checked_free(music_file_info->year);
13657     checked_free(music_file_info->played);
13658
13659     free(music_file_info);
13660
13661     music_file_info = next;
13662   }
13663
13664   new = &music_file_info;
13665
13666   // get (configured or unconfigured) music file info for all levels
13667   for (i = leveldir_current->first_level;
13668        i <= leveldir_current->last_level; i++)
13669   {
13670     int music_nr;
13671
13672     if (levelset.music[i] != MUS_UNDEFINED)
13673     {
13674       // get music file info for configured level music
13675       music_nr = levelset.music[i];
13676     }
13677     else if (num_music_noconf > 0)
13678     {
13679       // get music file info for unconfigured level music
13680       int level_pos = i - leveldir_current->first_level;
13681
13682       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13683     }
13684     else
13685     {
13686       continue;
13687     }
13688
13689     char *basename = getMusicInfoEntryFilename(music_nr);
13690
13691     if (basename == NULL)
13692       continue;
13693
13694     if (!music_info_listed(music_file_info, basename))
13695     {
13696       *new = get_music_file_info(basename, music_nr);
13697
13698       if (*new != NULL)
13699         new = &(*new)->next;
13700     }
13701   }
13702
13703   // get music file info for all remaining configured music files
13704   for (i = 0; i < num_music; i++)
13705   {
13706     music = getMusicListEntry(i);
13707
13708     if (music->filename == NULL)
13709       continue;
13710
13711     if (strEqual(music->filename, UNDEFINED_FILENAME))
13712       continue;
13713
13714     // a configured file may be not recognized as music
13715     if (!FileIsMusic(music->filename))
13716       continue;
13717
13718     if (!music_info_listed(music_file_info, music->filename))
13719     {
13720       *new = get_music_file_info(music->filename, i);
13721
13722       if (*new != NULL)
13723         new = &(*new)->next;
13724     }
13725   }
13726
13727   // get sound file info for all configured sound files
13728   for (i = 0; i < num_sounds; i++)
13729   {
13730     sound = getSoundListEntry(i);
13731
13732     if (sound->filename == NULL)
13733       continue;
13734
13735     if (strEqual(sound->filename, UNDEFINED_FILENAME))
13736       continue;
13737
13738     // a configured file may be not recognized as sound
13739     if (!FileIsSound(sound->filename))
13740       continue;
13741
13742     if (!sound_info_listed(music_file_info, sound->filename))
13743     {
13744       *new = get_sound_file_info(sound->filename, i);
13745       if (*new != NULL)
13746         new = &(*new)->next;
13747     }
13748   }
13749
13750   // add pointers to previous list nodes
13751
13752   struct MusicFileInfo *node = music_file_info;
13753
13754   while (node != NULL)
13755   {
13756     if (node->next)
13757       node->next->prev = node;
13758
13759     node = node->next;
13760   }
13761 }
13762
13763 static void add_helpanim_entry(int element, int action, int direction,
13764                                int delay, int *num_list_entries)
13765 {
13766   struct HelpAnimInfo *new_list_entry;
13767   (*num_list_entries)++;
13768
13769   helpanim_info =
13770     checked_realloc(helpanim_info,
13771                     *num_list_entries * sizeof(struct HelpAnimInfo));
13772   new_list_entry = &helpanim_info[*num_list_entries - 1];
13773
13774   new_list_entry->element = element;
13775   new_list_entry->action = action;
13776   new_list_entry->direction = direction;
13777   new_list_entry->delay = delay;
13778 }
13779
13780 static void print_unknown_token(char *filename, char *token, int token_nr)
13781 {
13782   if (token_nr == 0)
13783   {
13784     Warn("---");
13785     Warn("unknown token(s) found in config file:");
13786     Warn("- config file: '%s'", filename);
13787   }
13788
13789   Warn("- token: '%s'", token);
13790 }
13791
13792 static void print_unknown_token_end(int token_nr)
13793 {
13794   if (token_nr > 0)
13795     Warn("---");
13796 }
13797
13798 void LoadHelpAnimInfo(void)
13799 {
13800   char *filename = getHelpAnimFilename();
13801   SetupFileList *setup_file_list = NULL, *list;
13802   SetupFileHash *element_hash, *action_hash, *direction_hash;
13803   int num_list_entries = 0;
13804   int num_unknown_tokens = 0;
13805   int i;
13806
13807   if (fileExists(filename))
13808     setup_file_list = loadSetupFileList(filename);
13809
13810   if (setup_file_list == NULL)
13811   {
13812     // use reliable default values from static configuration
13813     SetupFileList *insert_ptr;
13814
13815     insert_ptr = setup_file_list =
13816       newSetupFileList(helpanim_config[0].token,
13817                        helpanim_config[0].value);
13818
13819     for (i = 1; helpanim_config[i].token; i++)
13820       insert_ptr = addListEntry(insert_ptr,
13821                                 helpanim_config[i].token,
13822                                 helpanim_config[i].value);
13823   }
13824
13825   element_hash   = newSetupFileHash();
13826   action_hash    = newSetupFileHash();
13827   direction_hash = newSetupFileHash();
13828
13829   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13830     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13831
13832   for (i = 0; i < NUM_ACTIONS; i++)
13833     setHashEntry(action_hash, element_action_info[i].suffix,
13834                  i_to_a(element_action_info[i].value));
13835
13836   // do not store direction index (bit) here, but direction value!
13837   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13838     setHashEntry(direction_hash, element_direction_info[i].suffix,
13839                  i_to_a(1 << element_direction_info[i].value));
13840
13841   for (list = setup_file_list; list != NULL; list = list->next)
13842   {
13843     char *element_token, *action_token, *direction_token;
13844     char *element_value, *action_value, *direction_value;
13845     int delay = atoi(list->value);
13846
13847     if (strEqual(list->token, "end"))
13848     {
13849       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13850
13851       continue;
13852     }
13853
13854     /* first try to break element into element/action/direction parts;
13855        if this does not work, also accept combined "element[.act][.dir]"
13856        elements (like "dynamite.active"), which are unique elements */
13857
13858     if (strchr(list->token, '.') == NULL)       // token contains no '.'
13859     {
13860       element_value = getHashEntry(element_hash, list->token);
13861       if (element_value != NULL)        // element found
13862         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13863                            &num_list_entries);
13864       else
13865       {
13866         // no further suffixes found -- this is not an element
13867         print_unknown_token(filename, list->token, num_unknown_tokens++);
13868       }
13869
13870       continue;
13871     }
13872
13873     // token has format "<prefix>.<something>"
13874
13875     action_token = strchr(list->token, '.');    // suffix may be action ...
13876     direction_token = action_token;             // ... or direction
13877
13878     element_token = getStringCopy(list->token);
13879     *strchr(element_token, '.') = '\0';
13880
13881     element_value = getHashEntry(element_hash, element_token);
13882
13883     if (element_value == NULL)          // this is no element
13884     {
13885       element_value = getHashEntry(element_hash, list->token);
13886       if (element_value != NULL)        // combined element found
13887         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13888                            &num_list_entries);
13889       else
13890         print_unknown_token(filename, list->token, num_unknown_tokens++);
13891
13892       free(element_token);
13893
13894       continue;
13895     }
13896
13897     action_value = getHashEntry(action_hash, action_token);
13898
13899     if (action_value != NULL)           // action found
13900     {
13901       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13902                     &num_list_entries);
13903
13904       free(element_token);
13905
13906       continue;
13907     }
13908
13909     direction_value = getHashEntry(direction_hash, direction_token);
13910
13911     if (direction_value != NULL)        // direction found
13912     {
13913       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13914                          &num_list_entries);
13915
13916       free(element_token);
13917
13918       continue;
13919     }
13920
13921     if (strchr(action_token + 1, '.') == NULL)
13922     {
13923       // no further suffixes found -- this is not an action nor direction
13924
13925       element_value = getHashEntry(element_hash, list->token);
13926       if (element_value != NULL)        // combined element found
13927         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13928                            &num_list_entries);
13929       else
13930         print_unknown_token(filename, list->token, num_unknown_tokens++);
13931
13932       free(element_token);
13933
13934       continue;
13935     }
13936
13937     // token has format "<prefix>.<suffix>.<something>"
13938
13939     direction_token = strchr(action_token + 1, '.');
13940
13941     action_token = getStringCopy(action_token);
13942     *strchr(action_token + 1, '.') = '\0';
13943
13944     action_value = getHashEntry(action_hash, action_token);
13945
13946     if (action_value == NULL)           // this is no action
13947     {
13948       element_value = getHashEntry(element_hash, list->token);
13949       if (element_value != NULL)        // combined element found
13950         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13951                            &num_list_entries);
13952       else
13953         print_unknown_token(filename, list->token, num_unknown_tokens++);
13954
13955       free(element_token);
13956       free(action_token);
13957
13958       continue;
13959     }
13960
13961     direction_value = getHashEntry(direction_hash, direction_token);
13962
13963     if (direction_value != NULL)        // direction found
13964     {
13965       add_helpanim_entry(atoi(element_value), atoi(action_value),
13966                          atoi(direction_value), delay, &num_list_entries);
13967
13968       free(element_token);
13969       free(action_token);
13970
13971       continue;
13972     }
13973
13974     // this is no direction
13975
13976     element_value = getHashEntry(element_hash, list->token);
13977     if (element_value != NULL)          // combined element found
13978       add_helpanim_entry(atoi(element_value), -1, -1, delay,
13979                          &num_list_entries);
13980     else
13981       print_unknown_token(filename, list->token, num_unknown_tokens++);
13982
13983     free(element_token);
13984     free(action_token);
13985   }
13986
13987   print_unknown_token_end(num_unknown_tokens);
13988
13989   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13990   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
13991
13992   freeSetupFileList(setup_file_list);
13993   freeSetupFileHash(element_hash);
13994   freeSetupFileHash(action_hash);
13995   freeSetupFileHash(direction_hash);
13996
13997 #if 0
13998   for (i = 0; i < num_list_entries; i++)
13999     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14000           EL_NAME(helpanim_info[i].element),
14001           helpanim_info[i].element,
14002           helpanim_info[i].action,
14003           helpanim_info[i].direction,
14004           helpanim_info[i].delay);
14005 #endif
14006 }
14007
14008 void LoadHelpTextInfo(void)
14009 {
14010   char *filename = getHelpTextFilename();
14011   int i;
14012
14013   if (helptext_info != NULL)
14014   {
14015     freeSetupFileHash(helptext_info);
14016     helptext_info = NULL;
14017   }
14018
14019   if (fileExists(filename))
14020     helptext_info = loadSetupFileHash(filename);
14021
14022   if (helptext_info == NULL)
14023   {
14024     // use reliable default values from static configuration
14025     helptext_info = newSetupFileHash();
14026
14027     for (i = 0; helptext_config[i].token; i++)
14028       setHashEntry(helptext_info,
14029                    helptext_config[i].token,
14030                    helptext_config[i].value);
14031   }
14032
14033 #if 0
14034   BEGIN_HASH_ITERATION(helptext_info, itr)
14035   {
14036     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14037           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14038   }
14039   END_HASH_ITERATION(hash, itr)
14040 #endif
14041 }
14042
14043
14044 // ----------------------------------------------------------------------------
14045 // convert levels
14046 // ----------------------------------------------------------------------------
14047
14048 #define MAX_NUM_CONVERT_LEVELS          1000
14049
14050 void ConvertLevels(void)
14051 {
14052   static LevelDirTree *convert_leveldir = NULL;
14053   static int convert_level_nr = -1;
14054   static int num_levels_handled = 0;
14055   static int num_levels_converted = 0;
14056   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14057   int i;
14058
14059   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14060                                                global.convert_leveldir);
14061
14062   if (convert_leveldir == NULL)
14063     Fail("no such level identifier: '%s'", global.convert_leveldir);
14064
14065   leveldir_current = convert_leveldir;
14066
14067   if (global.convert_level_nr != -1)
14068   {
14069     convert_leveldir->first_level = global.convert_level_nr;
14070     convert_leveldir->last_level  = global.convert_level_nr;
14071   }
14072
14073   convert_level_nr = convert_leveldir->first_level;
14074
14075   PrintLine("=", 79);
14076   Print("Converting levels\n");
14077   PrintLine("-", 79);
14078   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14079   Print("Level series name:       '%s'\n", convert_leveldir->name);
14080   Print("Level series author:     '%s'\n", convert_leveldir->author);
14081   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14082   PrintLine("=", 79);
14083   Print("\n");
14084
14085   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14086     levels_failed[i] = FALSE;
14087
14088   while (convert_level_nr <= convert_leveldir->last_level)
14089   {
14090     char *level_filename;
14091     boolean new_level;
14092
14093     level_nr = convert_level_nr++;
14094
14095     Print("Level %03d: ", level_nr);
14096
14097     LoadLevel(level_nr);
14098     if (level.no_level_file || level.no_valid_file)
14099     {
14100       Print("(no level)\n");
14101       continue;
14102     }
14103
14104     Print("converting level ... ");
14105
14106 #if 0
14107     // special case: conversion of some EMC levels as requested by ACME
14108     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14109 #endif
14110
14111     level_filename = getDefaultLevelFilename(level_nr);
14112     new_level = !fileExists(level_filename);
14113
14114     if (new_level)
14115     {
14116       SaveLevel(level_nr);
14117
14118       num_levels_converted++;
14119
14120       Print("converted.\n");
14121     }
14122     else
14123     {
14124       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14125         levels_failed[level_nr] = TRUE;
14126
14127       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14128     }
14129
14130     num_levels_handled++;
14131   }
14132
14133   Print("\n");
14134   PrintLine("=", 79);
14135   Print("Number of levels handled: %d\n", num_levels_handled);
14136   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14137          (num_levels_handled ?
14138           num_levels_converted * 100 / num_levels_handled : 0));
14139   PrintLine("-", 79);
14140   Print("Summary (for automatic parsing by scripts):\n");
14141   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14142          convert_leveldir->identifier, num_levels_converted,
14143          num_levels_handled,
14144          (num_levels_handled ?
14145           num_levels_converted * 100 / num_levels_handled : 0));
14146
14147   if (num_levels_handled != num_levels_converted)
14148   {
14149     Print(", FAILED:");
14150     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14151       if (levels_failed[i])
14152         Print(" %03d", i);
14153   }
14154
14155   Print("\n");
14156   PrintLine("=", 79);
14157
14158   CloseAllAndExit(0);
14159 }
14160
14161
14162 // ----------------------------------------------------------------------------
14163 // create and save images for use in level sketches (raw BMP format)
14164 // ----------------------------------------------------------------------------
14165
14166 void CreateLevelSketchImages(void)
14167 {
14168   Bitmap *bitmap1;
14169   Bitmap *bitmap2;
14170   int i;
14171
14172   InitElementPropertiesGfxElement();
14173
14174   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14175   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14176
14177   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14178   {
14179     int element = getMappedElement(i);
14180     char basename1[16];
14181     char basename2[16];
14182     char *filename1;
14183     char *filename2;
14184
14185     sprintf(basename1, "%04d.bmp", i);
14186     sprintf(basename2, "%04ds.bmp", i);
14187
14188     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14189     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14190
14191     DrawSizedElement(0, 0, element, TILESIZE);
14192     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14193
14194     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14195       Fail("cannot save level sketch image file '%s'", filename1);
14196
14197     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14198     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14199
14200     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14201       Fail("cannot save level sketch image file '%s'", filename2);
14202
14203     free(filename1);
14204     free(filename2);
14205
14206     // create corresponding SQL statements (for normal and small images)
14207     if (i < 1000)
14208     {
14209       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14210       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14211     }
14212
14213     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14214     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14215
14216     // optional: create content for forum level sketch demonstration post
14217     if (options.debug)
14218       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14219   }
14220
14221   FreeBitmap(bitmap1);
14222   FreeBitmap(bitmap2);
14223
14224   if (options.debug)
14225     fprintf(stderr, "\n");
14226
14227   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14228
14229   CloseAllAndExit(0);
14230 }
14231
14232
14233 // ----------------------------------------------------------------------------
14234 // create and save images for element collecting animations (raw BMP format)
14235 // ----------------------------------------------------------------------------
14236
14237 static boolean createCollectImage(int element)
14238 {
14239   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14240 }
14241
14242 void CreateCollectElementImages(void)
14243 {
14244   int i, j;
14245   int num_steps = 8;
14246   int anim_frames = num_steps - 1;
14247   int tile_size = TILESIZE;
14248   int anim_width  = tile_size * anim_frames;
14249   int anim_height = tile_size;
14250   int num_collect_images = 0;
14251   int pos_collect_images = 0;
14252
14253   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14254     if (createCollectImage(i))
14255       num_collect_images++;
14256
14257   Info("Creating %d element collecting animation images ...",
14258        num_collect_images);
14259
14260   int dst_width  = anim_width * 2;
14261   int dst_height = anim_height * num_collect_images / 2;
14262   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14263   char *basename_bmp = "RocksCollect.bmp";
14264   char *basename_png = "RocksCollect.png";
14265   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14266   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14267   int len_filename_bmp = strlen(filename_bmp);
14268   int len_filename_png = strlen(filename_png);
14269   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14270   char cmd_convert[max_command_len];
14271
14272   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14273            filename_bmp,
14274            filename_png);
14275
14276   // force using RGBA surface for destination bitmap
14277   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14278                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14279
14280   dst_bitmap->surface =
14281     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14282
14283   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14284   {
14285     if (!createCollectImage(i))
14286       continue;
14287
14288     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14289     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14290     int graphic = el2img(i);
14291     char *token_name = element_info[i].token_name;
14292     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14293     Bitmap *src_bitmap;
14294     int src_x, src_y;
14295
14296     Info("- creating collecting image for '%s' ...", token_name);
14297
14298     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14299
14300     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14301                tile_size, tile_size, 0, 0);
14302
14303     // force using RGBA surface for temporary bitmap (using transparent black)
14304     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14305                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14306
14307     tmp_bitmap->surface =
14308       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14309
14310     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14311
14312     for (j = 0; j < anim_frames; j++)
14313     {
14314       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14315       int frame_size = frame_size_final * num_steps;
14316       int offset = (tile_size - frame_size_final) / 2;
14317       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14318
14319       while (frame_size > frame_size_final)
14320       {
14321         frame_size /= 2;
14322
14323         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14324
14325         FreeBitmap(frame_bitmap);
14326
14327         frame_bitmap = half_bitmap;
14328       }
14329
14330       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14331                        frame_size_final, frame_size_final,
14332                        dst_x + j * tile_size + offset, dst_y + offset);
14333
14334       FreeBitmap(frame_bitmap);
14335     }
14336
14337     tmp_bitmap->surface_masked = NULL;
14338
14339     FreeBitmap(tmp_bitmap);
14340
14341     pos_collect_images++;
14342   }
14343
14344   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14345     Fail("cannot save element collecting image file '%s'", filename_bmp);
14346
14347   FreeBitmap(dst_bitmap);
14348
14349   Info("Converting image file from BMP to PNG ...");
14350
14351   if (system(cmd_convert) != 0)
14352     Fail("converting image file failed");
14353
14354   unlink(filename_bmp);
14355
14356   Info("Done.");
14357
14358   CloseAllAndExit(0);
14359 }
14360
14361
14362 // ----------------------------------------------------------------------------
14363 // create and save images for custom and group elements (raw BMP format)
14364 // ----------------------------------------------------------------------------
14365
14366 void CreateCustomElementImages(char *directory)
14367 {
14368   char *src_basename = "RocksCE-template.ilbm";
14369   char *dst_basename = "RocksCE.bmp";
14370   char *src_filename = getPath2(directory, src_basename);
14371   char *dst_filename = getPath2(directory, dst_basename);
14372   Bitmap *src_bitmap;
14373   Bitmap *bitmap;
14374   int yoffset_ce = 0;
14375   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14376   int i;
14377
14378   InitVideoDefaults();
14379
14380   ReCreateBitmap(&backbuffer, video.width, video.height);
14381
14382   src_bitmap = LoadImage(src_filename);
14383
14384   bitmap = CreateBitmap(TILEX * 16 * 2,
14385                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14386                         DEFAULT_DEPTH);
14387
14388   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14389   {
14390     int x = i % 16;
14391     int y = i / 16;
14392     int ii = i + 1;
14393     int j;
14394
14395     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14396                TILEX * x, TILEY * y + yoffset_ce);
14397
14398     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14399                TILEX, TILEY,
14400                TILEX * x + TILEX * 16,
14401                TILEY * y + yoffset_ce);
14402
14403     for (j = 2; j >= 0; j--)
14404     {
14405       int c = ii % 10;
14406
14407       BlitBitmap(src_bitmap, bitmap,
14408                  TILEX + c * 7, 0, 6, 10,
14409                  TILEX * x + 6 + j * 7,
14410                  TILEY * y + 11 + yoffset_ce);
14411
14412       BlitBitmap(src_bitmap, bitmap,
14413                  TILEX + c * 8, TILEY, 6, 10,
14414                  TILEX * 16 + TILEX * x + 6 + j * 8,
14415                  TILEY * y + 10 + yoffset_ce);
14416
14417       ii /= 10;
14418     }
14419   }
14420
14421   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14422   {
14423     int x = i % 16;
14424     int y = i / 16;
14425     int ii = i + 1;
14426     int j;
14427
14428     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14429                TILEX * x, TILEY * y + yoffset_ge);
14430
14431     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14432                TILEX, TILEY,
14433                TILEX * x + TILEX * 16,
14434                TILEY * y + yoffset_ge);
14435
14436     for (j = 1; j >= 0; j--)
14437     {
14438       int c = ii % 10;
14439
14440       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14441                  TILEX * x + 6 + j * 10,
14442                  TILEY * y + 11 + yoffset_ge);
14443
14444       BlitBitmap(src_bitmap, bitmap,
14445                  TILEX + c * 8, TILEY + 12, 6, 10,
14446                  TILEX * 16 + TILEX * x + 10 + j * 8,
14447                  TILEY * y + 10 + yoffset_ge);
14448
14449       ii /= 10;
14450     }
14451   }
14452
14453   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14454     Fail("cannot save CE graphics file '%s'", dst_filename);
14455
14456   FreeBitmap(bitmap);
14457
14458   CloseAllAndExit(0);
14459 }