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