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