renamed preprocessor constants
[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 "editor.h"
23 #include "tools.h"
24 #include "tape.h"
25 #include "config.h"
26 #include "api.h"
27
28 #define ENABLE_UNUSED_CODE      0       // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
30 #define ENABLE_RESERVED_CODE    0       // reserved for later use
31
32 #define CHUNK_ID_LEN            4       // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
35
36 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
38
39 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
50
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
53
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
58
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
61
62 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
65
66 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
67
68 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
71
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER                 0
79 #define SAVE_CONF_ALWAYS                1
80 #define SAVE_CONF_WHEN_CHANGED          -1
81
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE                0x00
84 #define CONF_MASK_2_BYTE                0x40
85 #define CONF_MASK_4_BYTE                0x80
86 #define CONF_MASK_MULTI_BYTES           0xc0
87
88 #define CONF_MASK_BYTES                 0xc0
89 #define CONF_MASK_TOKEN                 0x3f
90
91 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
92 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
93 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
94 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
95
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
101
102 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
103                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
104                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
105
106 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES          (2)
109
110 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
111                                          (t) == TYPE_ELEMENT_LIST ?     \
112                                          CONF_ELEMENT_NUM_BYTES :       \
113                                          (t) == TYPE_CONTENT ||         \
114                                          (t) == TYPE_CONTENT_LIST ?     \
115                                          CONF_CONTENT_NUM_BYTES : 1)
116
117 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
122                                          (y) * 3 + (x))
123 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
124                                          CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
140
141 struct LevelFileConfigInfo
142 {
143   int element;                  // element for which data is to be stored
144   int save_type;                // save data always, never or when changed
145   int data_type;                // data type (used internally, not stored)
146   int conf_type;                // micro chunk identifier (stored in file)
147
148   // (mandatory)
149   void *value;                  // variable that holds the data to be stored
150   int default_value;            // initial default value for this variable
151
152   // (optional)
153   void *value_copy;             // variable that holds the data to be copied
154   void *num_entities;           // number of entities for multi-byte data
155   int default_num_entities;     // default number of entities for this data
156   int max_num_entities;         // maximal number of entities for this data
157   char *default_string;         // optional default string for string data
158 };
159
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 {
162   // ---------- values not related to single elements -------------------------
163
164   {
165     -1,                                 SAVE_CONF_ALWAYS,
166     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
167     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
168   },
169   {
170     -1,                                 SAVE_CONF_ALWAYS,
171     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
172     &li.fieldx,                         STD_LEV_FIELDX
173   },
174   {
175     -1,                                 SAVE_CONF_ALWAYS,
176     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
177     &li.fieldy,                         STD_LEV_FIELDY
178   },
179   {
180     -1,                                 SAVE_CONF_ALWAYS,
181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
182     &li.time,                           100
183   },
184   {
185     -1,                                 SAVE_CONF_ALWAYS,
186     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
187     &li.gems_needed,                    0
188   },
189   {
190     -1,                                 -1,
191     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
192     &li.random_seed,                    0
193   },
194   {
195     -1,                                 -1,
196     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
197     &li.use_step_counter,               FALSE
198   },
199   {
200     -1,                                 -1,
201     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
202     &li.wind_direction_initial,         MV_NONE
203   },
204   {
205     -1,                                 -1,
206     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
207     &li.em_slippery_gems,               FALSE
208   },
209   {
210     -1,                                 -1,
211     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
212     &li.use_custom_template,            FALSE
213   },
214   {
215     -1,                                 -1,
216     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
217     &li.can_move_into_acid_bits,        ~0      // default: everything can
218   },
219   {
220     -1,                                 -1,
221     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
222     &li.dont_collide_with_bits,         ~0      // default: always deadly
223   },
224   {
225     -1,                                 -1,
226     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
227     &li.em_explodes_by_fire,            FALSE
228   },
229   {
230     -1,                                 -1,
231     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
232     &li.score[SC_TIME_BONUS],           1
233   },
234   {
235     -1,                                 -1,
236     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
237     &li.auto_exit_sokoban,              FALSE
238   },
239   {
240     -1,                                 -1,
241     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
242     &li.auto_count_gems,                FALSE
243   },
244   {
245     -1,                                 -1,
246     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
247     &li.solved_by_one_player,           FALSE
248   },
249   {
250     -1,                                 -1,
251     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
252     &li.time_score_base,                1
253   },
254   {
255     -1,                                 -1,
256     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
257     &li.rate_time_over_score,           FALSE
258   },
259   {
260     -1,                                 -1,
261     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
262     &li.bd_intermission,                FALSE
263   },
264   {
265     -1,                                 -1,
266     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
267     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
268   },
269   {
270     -1,                                 -1,
271     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
272     &li.bd_pal_timing,                  FALSE
273   },
274   {
275     -1,                                 -1,
276     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
277     &li.bd_cycle_delay_ms,              200
278   },
279   {
280     -1,                                 -1,
281     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
282     &li.bd_cycle_delay_c64,             0
283   },
284   {
285     -1,                                 -1,
286     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
287     &li.bd_hatching_delay_cycles,       21
288   },
289   {
290     -1,                                 -1,
291     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
292     &li.bd_hatching_delay_seconds,      2
293   },
294   {
295     -1,                                 -1,
296     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
297     &li.bd_line_shifting_borders,       FALSE
298   },
299   {
300     -1,                                 -1,
301     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
302     &li.bd_scan_first_and_last_row,     TRUE
303   },
304   {
305     -1,                                 -1,
306     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
307     &li.bd_short_explosions,            TRUE
308   },
309   {
310     -1,                                 -1,
311     TYPE_INTEGER,                       CONF_VALUE_8_BIT(23),
312     &li.bd_cave_random_seed_c64,        0
313   },
314   {
315     -1,                                 -1,
316     TYPE_INTEGER,                       CONF_VALUE_32_BIT(3),
317     &li.bd_color_b,                     GD_C64_COLOR(0)
318   },
319   {
320     -1,                                 -1,
321     TYPE_INTEGER,                       CONF_VALUE_32_BIT(4),
322     &li.bd_color_0,                     GD_C64_COLOR(0)
323   },
324   {
325     -1,                                 -1,
326     TYPE_INTEGER,                       CONF_VALUE_32_BIT(5),
327     &li.bd_color_1,                     GD_C64_COLOR(8)
328   },
329   {
330     -1,                                 -1,
331     TYPE_INTEGER,                       CONF_VALUE_32_BIT(6),
332     &li.bd_color_2,                     GD_C64_COLOR(11)
333   },
334   {
335     -1,                                 -1,
336     TYPE_INTEGER,                       CONF_VALUE_32_BIT(7),
337     &li.bd_color_3,                     GD_C64_COLOR(1)
338   },
339   {
340     -1,                                 -1,
341     TYPE_INTEGER,                       CONF_VALUE_32_BIT(8),
342     &li.bd_color_4,                     GD_C64_COLOR(5)
343   },
344   {
345     -1,                                 -1,
346     TYPE_INTEGER,                       CONF_VALUE_32_BIT(9),
347     &li.bd_color_5,                     GD_C64_COLOR(6)
348   },
349
350   {
351     -1,                                 -1,
352     -1,                                 -1,
353     NULL,                               -1
354   }
355 };
356
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
358 {
359   // (these values are the same for each player)
360   {
361     EL_PLAYER_1,                        -1,
362     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
363     &li.block_last_field,               FALSE   // default case for EM levels
364   },
365   {
366     EL_PLAYER_1,                        -1,
367     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
368     &li.sp_block_last_field,            TRUE    // default case for SP levels
369   },
370   {
371     EL_PLAYER_1,                        -1,
372     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
373     &li.instant_relocation,             FALSE
374   },
375   {
376     EL_PLAYER_1,                        -1,
377     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
378     &li.can_pass_to_walkable,           FALSE
379   },
380   {
381     EL_PLAYER_1,                        -1,
382     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
383     &li.block_snap_field,               TRUE
384   },
385   {
386     EL_PLAYER_1,                        -1,
387     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
388     &li.continuous_snapping,            TRUE
389   },
390   {
391     EL_PLAYER_1,                        -1,
392     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
393     &li.shifted_relocation,             FALSE
394   },
395   {
396     EL_PLAYER_1,                        -1,
397     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
398     &li.lazy_relocation,                FALSE
399   },
400   {
401     EL_PLAYER_1,                        -1,
402     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
403     &li.finish_dig_collect,             TRUE
404   },
405   {
406     EL_PLAYER_1,                        -1,
407     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
408     &li.keep_walkable_ce,               FALSE
409   },
410
411   // (these values are different for each player)
412   {
413     EL_PLAYER_1,                        -1,
414     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
415     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
416   },
417   {
418     EL_PLAYER_1,                        -1,
419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
420     &li.initial_player_gravity[0],      FALSE
421   },
422   {
423     EL_PLAYER_1,                        -1,
424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
425     &li.use_start_element[0],           FALSE
426   },
427   {
428     EL_PLAYER_1,                        -1,
429     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
430     &li.start_element[0],               EL_PLAYER_1
431   },
432   {
433     EL_PLAYER_1,                        -1,
434     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
435     &li.use_artwork_element[0],         FALSE
436   },
437   {
438     EL_PLAYER_1,                        -1,
439     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
440     &li.artwork_element[0],             EL_PLAYER_1
441   },
442   {
443     EL_PLAYER_1,                        -1,
444     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
445     &li.use_explosion_element[0],       FALSE
446   },
447   {
448     EL_PLAYER_1,                        -1,
449     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
450     &li.explosion_element[0],           EL_PLAYER_1
451   },
452   {
453     EL_PLAYER_1,                        -1,
454     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
455     &li.use_initial_inventory[0],       FALSE
456   },
457   {
458     EL_PLAYER_1,                        -1,
459     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
460     &li.initial_inventory_size[0],      1
461   },
462   {
463     EL_PLAYER_1,                        -1,
464     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
465     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
467   },
468
469   {
470     EL_PLAYER_2,                        -1,
471     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
472     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
473   },
474   {
475     EL_PLAYER_2,                        -1,
476     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
477     &li.initial_player_gravity[1],      FALSE
478   },
479   {
480     EL_PLAYER_2,                        -1,
481     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
482     &li.use_start_element[1],           FALSE
483   },
484   {
485     EL_PLAYER_2,                        -1,
486     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
487     &li.start_element[1],               EL_PLAYER_2
488   },
489   {
490     EL_PLAYER_2,                        -1,
491     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
492     &li.use_artwork_element[1],         FALSE
493   },
494   {
495     EL_PLAYER_2,                        -1,
496     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
497     &li.artwork_element[1],             EL_PLAYER_2
498   },
499   {
500     EL_PLAYER_2,                        -1,
501     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
502     &li.use_explosion_element[1],       FALSE
503   },
504   {
505     EL_PLAYER_2,                        -1,
506     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
507     &li.explosion_element[1],           EL_PLAYER_2
508   },
509   {
510     EL_PLAYER_2,                        -1,
511     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
512     &li.use_initial_inventory[1],       FALSE
513   },
514   {
515     EL_PLAYER_2,                        -1,
516     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
517     &li.initial_inventory_size[1],      1
518   },
519   {
520     EL_PLAYER_2,                        -1,
521     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
522     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
524   },
525
526   {
527     EL_PLAYER_3,                        -1,
528     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
529     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
530   },
531   {
532     EL_PLAYER_3,                        -1,
533     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
534     &li.initial_player_gravity[2],      FALSE
535   },
536   {
537     EL_PLAYER_3,                        -1,
538     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
539     &li.use_start_element[2],           FALSE
540   },
541   {
542     EL_PLAYER_3,                        -1,
543     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
544     &li.start_element[2],               EL_PLAYER_3
545   },
546   {
547     EL_PLAYER_3,                        -1,
548     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
549     &li.use_artwork_element[2],         FALSE
550   },
551   {
552     EL_PLAYER_3,                        -1,
553     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
554     &li.artwork_element[2],             EL_PLAYER_3
555   },
556   {
557     EL_PLAYER_3,                        -1,
558     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
559     &li.use_explosion_element[2],       FALSE
560   },
561   {
562     EL_PLAYER_3,                        -1,
563     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
564     &li.explosion_element[2],           EL_PLAYER_3
565   },
566   {
567     EL_PLAYER_3,                        -1,
568     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
569     &li.use_initial_inventory[2],       FALSE
570   },
571   {
572     EL_PLAYER_3,                        -1,
573     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
574     &li.initial_inventory_size[2],      1
575   },
576   {
577     EL_PLAYER_3,                        -1,
578     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
579     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
581   },
582
583   {
584     EL_PLAYER_4,                        -1,
585     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
586     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
587   },
588   {
589     EL_PLAYER_4,                        -1,
590     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
591     &li.initial_player_gravity[3],      FALSE
592   },
593   {
594     EL_PLAYER_4,                        -1,
595     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
596     &li.use_start_element[3],           FALSE
597   },
598   {
599     EL_PLAYER_4,                        -1,
600     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
601     &li.start_element[3],               EL_PLAYER_4
602   },
603   {
604     EL_PLAYER_4,                        -1,
605     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
606     &li.use_artwork_element[3],         FALSE
607   },
608   {
609     EL_PLAYER_4,                        -1,
610     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
611     &li.artwork_element[3],             EL_PLAYER_4
612   },
613   {
614     EL_PLAYER_4,                        -1,
615     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
616     &li.use_explosion_element[3],       FALSE
617   },
618   {
619     EL_PLAYER_4,                        -1,
620     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
621     &li.explosion_element[3],           EL_PLAYER_4
622   },
623   {
624     EL_PLAYER_4,                        -1,
625     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
626     &li.use_initial_inventory[3],       FALSE
627   },
628   {
629     EL_PLAYER_4,                        -1,
630     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
631     &li.initial_inventory_size[3],      1
632   },
633   {
634     EL_PLAYER_4,                        -1,
635     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
636     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
638   },
639
640   // (these values are only valid for BD style levels)
641   // (some values for BD style amoeba following below)
642   {
643     EL_BD_PLAYER,                       -1,
644     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
645     &li.bd_diagonal_movements,          FALSE
646   },
647   {
648     EL_BD_PLAYER,                       -1,
649     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
650     &li.bd_topmost_player_active,       TRUE
651   },
652   {
653     EL_BD_PLAYER,                       -1,
654     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
655     &li.bd_pushing_prob,                25
656   },
657   {
658     EL_BD_PLAYER,                       -1,
659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
660     &li.bd_pushing_prob_with_sweet,     100
661   },
662   {
663     EL_BD_PLAYER,                       -1,
664     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
665     &li.bd_push_mega_rock_with_sweet,   FALSE
666   },
667   {
668     EL_BD_PLAYER,                       -1,
669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
670     &li.bd_snap_element,                EL_EMPTY
671   },
672
673   {
674     EL_BD_SAND,                         -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
676     &li.bd_sand_looks_like,             EL_BD_SAND
677   },
678
679   {
680     EL_BD_ROCK,                         -1,
681     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
682     &li.bd_rock_turns_to_on_falling,    EL_BD_ROCK_FALLING
683   },
684   {
685     EL_BD_ROCK,                         -1,
686     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
687     &li.bd_rock_turns_to_on_impact,     EL_BD_ROCK
688   },
689
690   {
691     EL_BD_DIAMOND,                      -1,
692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
693     &li.score[SC_DIAMOND_EXTRA],        20
694   },
695   {
696     EL_BD_DIAMOND,                      -1,
697     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
698     &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
699   },
700   {
701     EL_BD_DIAMOND,                      -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
703     &li.bd_diamond_turns_to_on_impact,  EL_BD_DIAMOND
704   },
705
706   {
707     EL_BD_FIREFLY,                      -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_firefly_explodes_to,         EL_BD_EXPLODING_1
710   },
711
712   {
713     EL_BD_FIREFLY_2,                    -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_firefly_2_explodes_to,       EL_BD_EXPLODING_1
716   },
717
718   {
719     EL_BD_BUTTERFLY,                    -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_butterfly_explodes_to,       EL_BD_DIAMOND_GROWING_1
722   },
723
724   {
725     EL_BD_BUTTERFLY_2,                  -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_butterfly_2_explodes_to,     EL_BD_DIAMOND_GROWING_1
728   },
729
730   {
731     EL_BD_STONEFLY,                     -1,
732     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
733     &li.bd_stonefly_explodes_to,        EL_BD_ROCK_GROWING_1
734   },
735
736   {
737     EL_BD_DRAGONFLY,                    -1,
738     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
739     &li.bd_dragonfly_explodes_to,       EL_BD_EXPLODING_1
740   },
741
742   {
743     EL_BD_DIAMOND_GROWING_5,            -1,
744     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
745     &li.bd_diamond_birth_turns_to,      EL_BD_DIAMOND
746   },
747
748   {
749     EL_BD_BOMB_EXPLODING_4,             -1,
750     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
751     &li.bd_bomb_explosion_turns_to,     EL_BD_WALL
752   },
753
754   {
755     EL_BD_NITRO_PACK_EXPLODING_4,       -1,
756     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
757     &li.bd_nitro_explosion_turns_to,    EL_EMPTY
758   },
759
760   {
761     EL_BD_EXPLODING_5,                  -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
763     &li.bd_explosion_turns_to,          EL_EMPTY
764   },
765
766   {
767     EL_BD_MAGIC_WALL,                   -1,
768     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
769     &li.bd_magic_wall_wait_hatching,    FALSE
770   },
771   {
772     EL_BD_MAGIC_WALL,                   -1,
773     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
774     &li.bd_magic_wall_stops_amoeba,     TRUE
775   },
776   {
777     EL_BD_MAGIC_WALL,                   -1,
778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
779     &li.bd_magic_wall_zero_infinite,    TRUE
780   },
781   {
782     EL_BD_MAGIC_WALL,                   -1,
783     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
784     &li.bd_magic_wall_break_scan,       FALSE
785   },
786   {
787     EL_BD_MAGIC_WALL,                   -1,
788     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
789     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
790   },
791   {
792     EL_BD_MAGIC_WALL,                   -1,
793     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
794     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
795   },
796   {
797     EL_BD_MAGIC_WALL,                   -1,
798     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
799     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
800   },
801   {
802     EL_BD_MAGIC_WALL,                   -1,
803     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
804     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
805   },
806   {
807     EL_BD_MAGIC_WALL,                   -1,
808     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
809     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
810   },
811   {
812     EL_BD_MAGIC_WALL,                   -1,
813     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
814     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
815   },
816   {
817     EL_BD_MAGIC_WALL,                   -1,
818     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
819     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
820   },
821
822   {
823     EL_BD_CLOCK,                        -1,
824     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
825     &li.bd_clock_extra_time,            30
826   },
827
828   {
829     EL_BD_VOODOO_DOLL,                  -1,
830     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
831     &li.bd_voodoo_collects_diamonds,    FALSE
832   },
833   {
834     EL_BD_VOODOO_DOLL,                  -1,
835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
836     &li.bd_voodoo_hurt_kills_player,    FALSE
837   },
838   {
839     EL_BD_VOODOO_DOLL,                  -1,
840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
841     &li.bd_voodoo_dies_by_rock,         FALSE
842   },
843   {
844     EL_BD_VOODOO_DOLL,                  -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
846     &li.bd_voodoo_vanish_by_explosion,  TRUE
847   },
848   {
849     EL_BD_VOODOO_DOLL,                  -1,
850     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
851     &li.bd_voodoo_penalty_time,         30
852   },
853
854   {
855     EL_BD_SLIME,                        -1,
856     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
857     &li.bd_slime_is_predictable,        TRUE
858   },
859   {
860     EL_BD_SLIME,                        -1,
861     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
862     &li.bd_slime_permeability_rate,     100
863   },
864   {
865     EL_BD_SLIME,                        -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
867     &li.bd_slime_permeability_bits_c64, 0
868   },
869   {
870     EL_BD_SLIME,                        -1,
871     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
872     &li.bd_slime_random_seed_c64,       -1
873   },
874   {
875     EL_BD_SLIME,                        -1,
876     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
877     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
878   },
879   {
880     EL_BD_SLIME,                        -1,
881     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
882     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
883   },
884   {
885     EL_BD_SLIME,                        -1,
886     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
887     &li.bd_slime_eats_element_2,        EL_BD_ROCK
888   },
889   {
890     EL_BD_SLIME,                        -1,
891     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
892     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
893   },
894   {
895     EL_BD_SLIME,                        -1,
896     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
897     &li.bd_slime_eats_element_3,        EL_BD_NUT
898   },
899   {
900     EL_BD_SLIME,                        -1,
901     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
902     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
903   },
904
905   {
906     EL_BD_ACID,                         -1,
907     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
908     &li.bd_acid_eats_element,           EL_BD_SAND
909   },
910   {
911     EL_BD_ACID,                         -1,
912     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
913     &li.bd_acid_spread_rate,            3
914   },
915   {
916     EL_BD_ACID,                         -1,
917     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
918     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
919   },
920
921   {
922     EL_BD_BITER,                        -1,
923     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
924     &li.bd_biter_move_delay,            0
925   },
926   {
927     EL_BD_BITER,                        -1,
928     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
929     &li.bd_biter_eats_element,          EL_BD_DIAMOND
930   },
931
932   {
933     EL_BD_BLADDER,                      -1,
934     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
935     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
936   },
937
938   {
939     EL_BD_EXPANDABLE_WALL_ANY,          -1,
940     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
941     &li.bd_change_expanding_wall,       FALSE
942   },
943   {
944     EL_BD_EXPANDABLE_WALL_ANY,          -1,
945     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
946     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
947   },
948
949   {
950     EL_BD_REPLICATOR,                   -1,
951     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
952     &li.bd_replicators_active,          TRUE
953   },
954   {
955     EL_BD_REPLICATOR,                   -1,
956     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
957     &li.bd_replicator_create_delay,     4
958   },
959
960   {
961     EL_BD_CONVEYOR_LEFT,                -1,
962     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
963     &li.bd_conveyor_belts_active,       TRUE
964   },
965   {
966     EL_BD_CONVEYOR_LEFT,                -1,
967     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
968     &li.bd_conveyor_belts_changed,      FALSE
969   },
970
971   {
972     EL_BD_WATER,                        -1,
973     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
974     &li.bd_water_cannot_flow_down,      FALSE
975   },
976
977   {
978     EL_BD_NUT,                          -1,
979     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
980     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
981   },
982
983   {
984     EL_BD_PNEUMATIC_HAMMER,             -1,
985     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
986     &li.bd_hammer_walls_break_delay,    5
987   },
988   {
989     EL_BD_PNEUMATIC_HAMMER,             -1,
990     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
991     &li.bd_hammer_walls_reappear,       FALSE
992   },
993   {
994     EL_BD_PNEUMATIC_HAMMER,             -1,
995     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
996     &li.bd_hammer_walls_reappear_delay, 100
997   },
998
999   {
1000     EL_BD_ROCKET_LAUNCHER,              -1,
1001     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1002     &li.bd_infinite_rockets,            FALSE
1003   },
1004
1005   {
1006     EL_BD_SKELETON,                     -1,
1007     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1008     &li.bd_num_skeletons_needed_for_pot, 5
1009   },
1010   {
1011     EL_BD_SKELETON,                     -1,
1012     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1013     &li.bd_skeleton_worth_num_diamonds, 0
1014   },
1015
1016   {
1017     EL_BD_CREATURE_SWITCH,              -1,
1018     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1019     &li.bd_creatures_start_backwards,   FALSE
1020   },
1021   {
1022     EL_BD_CREATURE_SWITCH,              -1,
1023     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1024     &li.bd_creatures_turn_on_hatching,  FALSE
1025   },
1026   {
1027     EL_BD_CREATURE_SWITCH,              -1,
1028     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1029     &li.bd_creatures_auto_turn_delay,   0
1030   },
1031
1032   {
1033     EL_BD_GRAVITY_SWITCH,               -1,
1034     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1035     &li.bd_gravity_direction,           GD_MV_DOWN
1036   },
1037   {
1038     EL_BD_GRAVITY_SWITCH,               -1,
1039     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1040     &li.bd_gravity_switch_active,       FALSE
1041   },
1042   {
1043     EL_BD_GRAVITY_SWITCH,               -1,
1044     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1045     &li.bd_gravity_switch_delay,        10
1046   },
1047   {
1048     EL_BD_GRAVITY_SWITCH,               -1,
1049     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1050     &li.bd_gravity_affects_all,         TRUE
1051   },
1052
1053   // (the following values are related to various game elements)
1054
1055   {
1056     EL_EMERALD,                         -1,
1057     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1058     &li.score[SC_EMERALD],              10
1059   },
1060
1061   {
1062     EL_DIAMOND,                         -1,
1063     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1064     &li.score[SC_DIAMOND],              10
1065   },
1066
1067   {
1068     EL_BUG,                             -1,
1069     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1070     &li.score[SC_BUG],                  10
1071   },
1072
1073   {
1074     EL_SPACESHIP,                       -1,
1075     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1076     &li.score[SC_SPACESHIP],            10
1077   },
1078
1079   {
1080     EL_PACMAN,                          -1,
1081     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1082     &li.score[SC_PACMAN],               10
1083   },
1084
1085   {
1086     EL_NUT,                             -1,
1087     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1088     &li.score[SC_NUT],                  10
1089   },
1090
1091   {
1092     EL_DYNAMITE,                        -1,
1093     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1094     &li.score[SC_DYNAMITE],             10
1095   },
1096
1097   {
1098     EL_KEY_1,                           -1,
1099     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1100     &li.score[SC_KEY],                  10
1101   },
1102
1103   {
1104     EL_PEARL,                           -1,
1105     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1106     &li.score[SC_PEARL],                10
1107   },
1108
1109   {
1110     EL_CRYSTAL,                         -1,
1111     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1112     &li.score[SC_CRYSTAL],              10
1113   },
1114
1115   // (amoeba values used by R'n'D game engine only)
1116   {
1117     EL_BD_AMOEBA,                       -1,
1118     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1119     &li.amoeba_content,                 EL_DIAMOND
1120   },
1121   {
1122     EL_BD_AMOEBA,                       -1,
1123     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1124     &li.amoeba_speed,                   10
1125   },
1126   {
1127     EL_BD_AMOEBA,                       -1,
1128     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1129     &li.grow_into_diggable,             TRUE
1130   },
1131   // (amoeba values used by BD game engine only)
1132   {
1133     EL_BD_AMOEBA,                       -1,
1134     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1135     &li.bd_amoeba_wait_for_hatching,    FALSE
1136   },
1137   {
1138     EL_BD_AMOEBA,                       -1,
1139     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1140     &li.bd_amoeba_start_immediately,    TRUE
1141   },
1142   {
1143     EL_BD_AMOEBA,                       -1,
1144     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1145     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1146   },
1147   {
1148     EL_BD_AMOEBA,                       -1,
1149     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1150     &li.bd_amoeba_threshold_too_big,    200
1151   },
1152   {
1153     EL_BD_AMOEBA,                       -1,
1154     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1155     &li.bd_amoeba_slow_growth_time,     200
1156   },
1157   {
1158     EL_BD_AMOEBA,                       -1,
1159     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1160     &li.bd_amoeba_slow_growth_rate,     3
1161   },
1162   {
1163     EL_BD_AMOEBA,                       -1,
1164     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1165     &li.bd_amoeba_fast_growth_rate,     25
1166   },
1167   {
1168     EL_BD_AMOEBA,                       -1,
1169     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1170     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1171   },
1172   {
1173     EL_BD_AMOEBA,                       -1,
1174     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1175     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1176   },
1177
1178   {
1179     EL_BD_AMOEBA_2,                     -1,
1180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1181     &li.bd_amoeba_2_threshold_too_big,  200
1182   },
1183   {
1184     EL_BD_AMOEBA_2,                     -1,
1185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1186     &li.bd_amoeba_2_slow_growth_time,   200
1187   },
1188   {
1189     EL_BD_AMOEBA_2,                     -1,
1190     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1191     &li.bd_amoeba_2_slow_growth_rate,   3
1192   },
1193   {
1194     EL_BD_AMOEBA_2,                     -1,
1195     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1196     &li.bd_amoeba_2_fast_growth_rate,   25
1197   },
1198   {
1199     EL_BD_AMOEBA_2,                     -1,
1200     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1201     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1202   },
1203   {
1204     EL_BD_AMOEBA_2,                     -1,
1205     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1206     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1207   },
1208   {
1209     EL_BD_AMOEBA_2,                     -1,
1210     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1211     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1212   },
1213   {
1214     EL_BD_AMOEBA_2,                     -1,
1215     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1216     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1217   },
1218
1219   {
1220     EL_YAMYAM,                          -1,
1221     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1222     &li.yamyam_content,                 EL_ROCK, NULL,
1223     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1224   },
1225   {
1226     EL_YAMYAM,                          -1,
1227     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1228     &li.score[SC_YAMYAM],               10
1229   },
1230
1231   {
1232     EL_ROBOT,                           -1,
1233     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1234     &li.score[SC_ROBOT],                10
1235   },
1236   {
1237     EL_ROBOT,                           -1,
1238     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1239     &li.slurp_score,                    10
1240   },
1241
1242   {
1243     EL_ROBOT_WHEEL,                     -1,
1244     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1245     &li.time_wheel,                     10
1246   },
1247
1248   {
1249     EL_MAGIC_WALL,                      -1,
1250     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1251     &li.time_magic_wall,                10
1252   },
1253
1254   {
1255     EL_GAME_OF_LIFE,                    -1,
1256     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1257     &li.game_of_life[0],                2
1258   },
1259   {
1260     EL_GAME_OF_LIFE,                    -1,
1261     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1262     &li.game_of_life[1],                3
1263   },
1264   {
1265     EL_GAME_OF_LIFE,                    -1,
1266     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1267     &li.game_of_life[2],                3
1268   },
1269   {
1270     EL_GAME_OF_LIFE,                    -1,
1271     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1272     &li.game_of_life[3],                3
1273   },
1274   {
1275     EL_GAME_OF_LIFE,                    -1,
1276     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1277     &li.use_life_bugs,                  FALSE
1278   },
1279
1280   {
1281     EL_BIOMAZE,                         -1,
1282     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1283     &li.biomaze[0],                     2
1284   },
1285   {
1286     EL_BIOMAZE,                         -1,
1287     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1288     &li.biomaze[1],                     3
1289   },
1290   {
1291     EL_BIOMAZE,                         -1,
1292     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1293     &li.biomaze[2],                     3
1294   },
1295   {
1296     EL_BIOMAZE,                         -1,
1297     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1298     &li.biomaze[3],                     3
1299   },
1300
1301   {
1302     EL_TIMEGATE_SWITCH,                 -1,
1303     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1304     &li.time_timegate,                  10
1305   },
1306
1307   {
1308     EL_LIGHT_SWITCH_ACTIVE,             -1,
1309     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1310     &li.time_light,                     10
1311   },
1312
1313   {
1314     EL_SHIELD_NORMAL,                   -1,
1315     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1316     &li.shield_normal_time,             10
1317   },
1318   {
1319     EL_SHIELD_NORMAL,                   -1,
1320     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1321     &li.score[SC_SHIELD],               10
1322   },
1323
1324   {
1325     EL_SHIELD_DEADLY,                   -1,
1326     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1327     &li.shield_deadly_time,             10
1328   },
1329   {
1330     EL_SHIELD_DEADLY,                   -1,
1331     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1332     &li.score[SC_SHIELD],               10
1333   },
1334
1335   {
1336     EL_EXTRA_TIME,                      -1,
1337     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1338     &li.extra_time,                     10
1339   },
1340   {
1341     EL_EXTRA_TIME,                      -1,
1342     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1343     &li.extra_time_score,               10
1344   },
1345
1346   {
1347     EL_TIME_ORB_FULL,                   -1,
1348     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1349     &li.time_orb_time,                  10
1350   },
1351   {
1352     EL_TIME_ORB_FULL,                   -1,
1353     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1354     &li.use_time_orb_bug,               FALSE
1355   },
1356
1357   {
1358     EL_SPRING,                          -1,
1359     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1360     &li.use_spring_bug,                 FALSE
1361   },
1362
1363   {
1364     EL_EMC_ANDROID,                     -1,
1365     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1366     &li.android_move_time,              10
1367   },
1368   {
1369     EL_EMC_ANDROID,                     -1,
1370     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1371     &li.android_clone_time,             10
1372   },
1373   {
1374     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1375     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1376     &li.android_clone_element[0],       EL_EMPTY, NULL,
1377     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1378   },
1379   {
1380     EL_EMC_ANDROID,                     -1,
1381     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1382     &li.android_clone_element[0],       EL_EMPTY, NULL,
1383     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1384   },
1385
1386   {
1387     EL_EMC_LENSES,                      -1,
1388     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1389     &li.lenses_score,                   10
1390   },
1391   {
1392     EL_EMC_LENSES,                      -1,
1393     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1394     &li.lenses_time,                    10
1395   },
1396
1397   {
1398     EL_EMC_MAGNIFIER,                   -1,
1399     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1400     &li.magnify_score,                  10
1401   },
1402   {
1403     EL_EMC_MAGNIFIER,                   -1,
1404     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1405     &li.magnify_time,                   10
1406   },
1407
1408   {
1409     EL_EMC_MAGIC_BALL,                  -1,
1410     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1411     &li.ball_time,                      10
1412   },
1413   {
1414     EL_EMC_MAGIC_BALL,                  -1,
1415     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1416     &li.ball_random,                    FALSE
1417   },
1418   {
1419     EL_EMC_MAGIC_BALL,                  -1,
1420     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1421     &li.ball_active_initial,            FALSE
1422   },
1423   {
1424     EL_EMC_MAGIC_BALL,                  -1,
1425     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1426     &li.ball_content,                   EL_EMPTY, NULL,
1427     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1428   },
1429
1430   {
1431     EL_SOKOBAN_FIELD_EMPTY,             -1,
1432     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1433     &li.sb_fields_needed,               TRUE
1434   },
1435
1436   {
1437     EL_SOKOBAN_OBJECT,                  -1,
1438     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1439     &li.sb_objects_needed,              TRUE
1440   },
1441
1442   {
1443     EL_MM_MCDUFFIN,                     -1,
1444     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1445     &li.mm_laser_red,                   FALSE
1446   },
1447   {
1448     EL_MM_MCDUFFIN,                     -1,
1449     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1450     &li.mm_laser_green,                 FALSE
1451   },
1452   {
1453     EL_MM_MCDUFFIN,                     -1,
1454     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1455     &li.mm_laser_blue,                  TRUE
1456   },
1457
1458   {
1459     EL_DF_LASER,                        -1,
1460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1461     &li.df_laser_red,                   TRUE
1462   },
1463   {
1464     EL_DF_LASER,                        -1,
1465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1466     &li.df_laser_green,                 TRUE
1467   },
1468   {
1469     EL_DF_LASER,                        -1,
1470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1471     &li.df_laser_blue,                  FALSE
1472   },
1473
1474   {
1475     EL_MM_FUSE_ACTIVE,                  -1,
1476     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1477     &li.mm_time_fuse,                   25
1478   },
1479   {
1480     EL_MM_BOMB,                         -1,
1481     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1482     &li.mm_time_bomb,                   75
1483   },
1484
1485   {
1486     EL_MM_GRAY_BALL,                    -1,
1487     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1488     &li.mm_time_ball,                   75
1489   },
1490   {
1491     EL_MM_GRAY_BALL,                    -1,
1492     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1493     &li.mm_ball_choice_mode,            ANIM_RANDOM
1494   },
1495   {
1496     EL_MM_GRAY_BALL,                    -1,
1497     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1498     &li.mm_ball_content,                EL_EMPTY, NULL,
1499     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1500   },
1501   {
1502     EL_MM_GRAY_BALL,                    -1,
1503     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1504     &li.rotate_mm_ball_content,         TRUE
1505   },
1506   {
1507     EL_MM_GRAY_BALL,                    -1,
1508     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1509     &li.explode_mm_ball,                FALSE
1510   },
1511
1512   {
1513     EL_MM_STEEL_BLOCK,                  -1,
1514     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1515     &li.mm_time_block,                  75
1516   },
1517   {
1518     EL_MM_LIGHTBALL,                    -1,
1519     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1520     &li.score[SC_ELEM_BONUS],           10
1521   },
1522
1523   {
1524     -1,                                 -1,
1525     -1,                                 -1,
1526     NULL,                               -1
1527   }
1528 };
1529
1530 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1531 {
1532   {
1533     -1,                                 -1,
1534     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1535     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1536   },
1537   {
1538     -1,                                 -1,
1539     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1540     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1541   },
1542
1543   {
1544     -1,                                 -1,
1545     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1546     &xx_envelope.autowrap,              FALSE
1547   },
1548   {
1549     -1,                                 -1,
1550     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1551     &xx_envelope.centered,              FALSE
1552   },
1553
1554   {
1555     -1,                                 -1,
1556     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1557     &xx_envelope.text,                  -1, NULL,
1558     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1559     &xx_default_string_empty[0]
1560   },
1561
1562   {
1563     -1,                                 -1,
1564     -1,                                 -1,
1565     NULL,                               -1
1566   }
1567 };
1568
1569 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1570 {
1571   {
1572     -1,                                 -1,
1573     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1574     &xx_ei.description[0],              -1,
1575     &yy_ei.description[0],
1576     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1577     &xx_default_description[0]
1578   },
1579
1580   {
1581     -1,                                 -1,
1582     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1583     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1584     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1585   },
1586 #if ENABLE_RESERVED_CODE
1587   // (reserved for later use)
1588   {
1589     -1,                                 -1,
1590     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1591     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1592     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1593   },
1594 #endif
1595
1596   {
1597     -1,                                 -1,
1598     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1599     &xx_ei.use_gfx_element,             FALSE,
1600     &yy_ei.use_gfx_element
1601   },
1602   {
1603     -1,                                 -1,
1604     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1605     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1606     &yy_ei.gfx_element_initial
1607   },
1608
1609   {
1610     -1,                                 -1,
1611     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1612     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1613     &yy_ei.access_direction
1614   },
1615
1616   {
1617     -1,                                 -1,
1618     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1619     &xx_ei.collect_score_initial,       10,
1620     &yy_ei.collect_score_initial
1621   },
1622   {
1623     -1,                                 -1,
1624     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1625     &xx_ei.collect_count_initial,       1,
1626     &yy_ei.collect_count_initial
1627   },
1628
1629   {
1630     -1,                                 -1,
1631     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1632     &xx_ei.ce_value_fixed_initial,      0,
1633     &yy_ei.ce_value_fixed_initial
1634   },
1635   {
1636     -1,                                 -1,
1637     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1638     &xx_ei.ce_value_random_initial,     0,
1639     &yy_ei.ce_value_random_initial
1640   },
1641   {
1642     -1,                                 -1,
1643     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1644     &xx_ei.use_last_ce_value,           FALSE,
1645     &yy_ei.use_last_ce_value
1646   },
1647
1648   {
1649     -1,                                 -1,
1650     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1651     &xx_ei.push_delay_fixed,            8,
1652     &yy_ei.push_delay_fixed
1653   },
1654   {
1655     -1,                                 -1,
1656     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1657     &xx_ei.push_delay_random,           8,
1658     &yy_ei.push_delay_random
1659   },
1660   {
1661     -1,                                 -1,
1662     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1663     &xx_ei.drop_delay_fixed,            0,
1664     &yy_ei.drop_delay_fixed
1665   },
1666   {
1667     -1,                                 -1,
1668     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1669     &xx_ei.drop_delay_random,           0,
1670     &yy_ei.drop_delay_random
1671   },
1672   {
1673     -1,                                 -1,
1674     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1675     &xx_ei.move_delay_fixed,            0,
1676     &yy_ei.move_delay_fixed
1677   },
1678   {
1679     -1,                                 -1,
1680     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1681     &xx_ei.move_delay_random,           0,
1682     &yy_ei.move_delay_random
1683   },
1684   {
1685     -1,                                 -1,
1686     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1687     &xx_ei.step_delay_fixed,            0,
1688     &yy_ei.step_delay_fixed
1689   },
1690   {
1691     -1,                                 -1,
1692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1693     &xx_ei.step_delay_random,           0,
1694     &yy_ei.step_delay_random
1695   },
1696
1697   {
1698     -1,                                 -1,
1699     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1700     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1701     &yy_ei.move_pattern
1702   },
1703   {
1704     -1,                                 -1,
1705     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1706     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1707     &yy_ei.move_direction_initial
1708   },
1709   {
1710     -1,                                 -1,
1711     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1712     &xx_ei.move_stepsize,               TILEX / 8,
1713     &yy_ei.move_stepsize
1714   },
1715
1716   {
1717     -1,                                 -1,
1718     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1719     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1720     &yy_ei.move_enter_element
1721   },
1722   {
1723     -1,                                 -1,
1724     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1725     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1726     &yy_ei.move_leave_element
1727   },
1728   {
1729     -1,                                 -1,
1730     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1731     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1732     &yy_ei.move_leave_type
1733   },
1734
1735   {
1736     -1,                                 -1,
1737     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1738     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1739     &yy_ei.slippery_type
1740   },
1741
1742   {
1743     -1,                                 -1,
1744     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1745     &xx_ei.explosion_type,              EXPLODES_3X3,
1746     &yy_ei.explosion_type
1747   },
1748   {
1749     -1,                                 -1,
1750     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1751     &xx_ei.explosion_delay,             16,
1752     &yy_ei.explosion_delay
1753   },
1754   {
1755     -1,                                 -1,
1756     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1757     &xx_ei.ignition_delay,              8,
1758     &yy_ei.ignition_delay
1759   },
1760
1761   {
1762     -1,                                 -1,
1763     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1764     &xx_ei.content,                     EL_EMPTY_SPACE,
1765     &yy_ei.content,
1766     &xx_num_contents,                   1, 1
1767   },
1768
1769   // ---------- "num_change_pages" must be the last entry ---------------------
1770
1771   {
1772     -1,                                 SAVE_CONF_ALWAYS,
1773     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1774     &xx_ei.num_change_pages,            1,
1775     &yy_ei.num_change_pages
1776   },
1777
1778   {
1779     -1,                                 -1,
1780     -1,                                 -1,
1781     NULL,                               -1,
1782     NULL
1783   }
1784 };
1785
1786 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1787 {
1788   // ---------- "current_change_page" must be the first entry -----------------
1789
1790   {
1791     -1,                                 SAVE_CONF_ALWAYS,
1792     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1793     &xx_current_change_page,            -1
1794   },
1795
1796   // ---------- (the remaining entries can be in any order) -------------------
1797
1798   {
1799     -1,                                 -1,
1800     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1801     &xx_change.can_change,              FALSE
1802   },
1803
1804   {
1805     -1,                                 -1,
1806     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1807     &xx_event_bits[0],                  0
1808   },
1809   {
1810     -1,                                 -1,
1811     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1812     &xx_event_bits[1],                  0
1813   },
1814
1815   {
1816     -1,                                 -1,
1817     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1818     &xx_change.trigger_player,          CH_PLAYER_ANY
1819   },
1820   {
1821     -1,                                 -1,
1822     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1823     &xx_change.trigger_side,            CH_SIDE_ANY
1824   },
1825   {
1826     -1,                                 -1,
1827     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1828     &xx_change.trigger_page,            CH_PAGE_ANY
1829   },
1830
1831   {
1832     -1,                                 -1,
1833     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1834     &xx_change.target_element,          EL_EMPTY_SPACE
1835   },
1836
1837   {
1838     -1,                                 -1,
1839     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1840     &xx_change.delay_fixed,             0
1841   },
1842   {
1843     -1,                                 -1,
1844     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1845     &xx_change.delay_random,            0
1846   },
1847   {
1848     -1,                                 -1,
1849     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1850     &xx_change.delay_frames,            FRAMES_PER_SECOND
1851   },
1852
1853   {
1854     -1,                                 -1,
1855     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1856     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1857   },
1858
1859   {
1860     -1,                                 -1,
1861     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1862     &xx_change.explode,                 FALSE
1863   },
1864   {
1865     -1,                                 -1,
1866     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1867     &xx_change.use_target_content,      FALSE
1868   },
1869   {
1870     -1,                                 -1,
1871     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1872     &xx_change.only_if_complete,        FALSE
1873   },
1874   {
1875     -1,                                 -1,
1876     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1877     &xx_change.use_random_replace,      FALSE
1878   },
1879   {
1880     -1,                                 -1,
1881     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1882     &xx_change.random_percentage,       100
1883   },
1884   {
1885     -1,                                 -1,
1886     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1887     &xx_change.replace_when,            CP_WHEN_EMPTY
1888   },
1889
1890   {
1891     -1,                                 -1,
1892     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1893     &xx_change.has_action,              FALSE
1894   },
1895   {
1896     -1,                                 -1,
1897     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1898     &xx_change.action_type,             CA_NO_ACTION
1899   },
1900   {
1901     -1,                                 -1,
1902     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1903     &xx_change.action_mode,             CA_MODE_UNDEFINED
1904   },
1905   {
1906     -1,                                 -1,
1907     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1908     &xx_change.action_arg,              CA_ARG_UNDEFINED
1909   },
1910
1911   {
1912     -1,                                 -1,
1913     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1914     &xx_change.action_element,          EL_EMPTY_SPACE
1915   },
1916
1917   {
1918     -1,                                 -1,
1919     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1920     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1921     &xx_num_contents,                   1, 1
1922   },
1923
1924   {
1925     -1,                                 -1,
1926     -1,                                 -1,
1927     NULL,                               -1
1928   }
1929 };
1930
1931 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1932 {
1933   {
1934     -1,                                 -1,
1935     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1936     &xx_ei.description[0],              -1, NULL,
1937     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1938     &xx_default_description[0]
1939   },
1940
1941   {
1942     -1,                                 -1,
1943     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1944     &xx_ei.use_gfx_element,             FALSE
1945   },
1946   {
1947     -1,                                 -1,
1948     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1949     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1950   },
1951
1952   {
1953     -1,                                 -1,
1954     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1955     &xx_group.choice_mode,              ANIM_RANDOM
1956   },
1957
1958   {
1959     -1,                                 -1,
1960     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1961     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1962     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1963   },
1964
1965   {
1966     -1,                                 -1,
1967     -1,                                 -1,
1968     NULL,                               -1
1969   }
1970 };
1971
1972 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1973 {
1974   {
1975     -1,                                 -1,
1976     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1977     &xx_ei.use_gfx_element,             FALSE
1978   },
1979   {
1980     -1,                                 -1,
1981     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1982     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1983   },
1984
1985   {
1986     -1,                                 -1,
1987     -1,                                 -1,
1988     NULL,                               -1
1989   }
1990 };
1991
1992 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1993 {
1994   {
1995     EL_PLAYER_1,                        -1,
1996     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1997     &li.block_snap_field,               TRUE
1998   },
1999   {
2000     EL_PLAYER_1,                        -1,
2001     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
2002     &li.continuous_snapping,            TRUE
2003   },
2004   {
2005     EL_PLAYER_1,                        -1,
2006     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
2007     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
2008   },
2009   {
2010     EL_PLAYER_1,                        -1,
2011     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
2012     &li.use_start_element[0],           FALSE
2013   },
2014   {
2015     EL_PLAYER_1,                        -1,
2016     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
2017     &li.start_element[0],               EL_PLAYER_1
2018   },
2019   {
2020     EL_PLAYER_1,                        -1,
2021     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
2022     &li.use_artwork_element[0],         FALSE
2023   },
2024   {
2025     EL_PLAYER_1,                        -1,
2026     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
2027     &li.artwork_element[0],             EL_PLAYER_1
2028   },
2029   {
2030     EL_PLAYER_1,                        -1,
2031     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
2032     &li.use_explosion_element[0],       FALSE
2033   },
2034   {
2035     EL_PLAYER_1,                        -1,
2036     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
2037     &li.explosion_element[0],           EL_PLAYER_1
2038   },
2039
2040   {
2041     -1,                                 -1,
2042     -1,                                 -1,
2043     NULL,                               -1
2044   }
2045 };
2046
2047 static struct
2048 {
2049   int filetype;
2050   char *id;
2051 }
2052 filetype_id_list[] =
2053 {
2054   { LEVEL_FILE_TYPE_RND,        "RND"   },
2055   { LEVEL_FILE_TYPE_BD,         "BD"    },
2056   { LEVEL_FILE_TYPE_EM,         "EM"    },
2057   { LEVEL_FILE_TYPE_SP,         "SP"    },
2058   { LEVEL_FILE_TYPE_DX,         "DX"    },
2059   { LEVEL_FILE_TYPE_SB,         "SB"    },
2060   { LEVEL_FILE_TYPE_DC,         "DC"    },
2061   { LEVEL_FILE_TYPE_MM,         "MM"    },
2062   { LEVEL_FILE_TYPE_MM,         "DF"    },
2063   { -1,                         NULL    },
2064 };
2065
2066
2067 // ============================================================================
2068 // level file functions
2069 // ============================================================================
2070
2071 static boolean check_special_flags(char *flag)
2072 {
2073   if (strEqual(options.special_flags, flag) ||
2074       strEqual(leveldir_current->special_flags, flag))
2075     return TRUE;
2076
2077   return FALSE;
2078 }
2079
2080 static struct DateInfo getCurrentDate(void)
2081 {
2082   time_t epoch_seconds = time(NULL);
2083   struct tm *now = localtime(&epoch_seconds);
2084   struct DateInfo date;
2085
2086   date.year  = now->tm_year + 1900;
2087   date.month = now->tm_mon  + 1;
2088   date.day   = now->tm_mday;
2089
2090   date.src   = DATE_SRC_CLOCK;
2091
2092   return date;
2093 }
2094
2095 static void resetEventFlags(struct ElementChangeInfo *change)
2096 {
2097   int i;
2098
2099   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2100     change->has_event[i] = FALSE;
2101 }
2102
2103 static void resetEventBits(void)
2104 {
2105   int i;
2106
2107   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2108     xx_event_bits[i] = 0;
2109 }
2110
2111 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2112 {
2113   int i;
2114
2115   /* important: only change event flag if corresponding event bit is set
2116      (this is because all xx_event_bits[] values are loaded separately,
2117      and all xx_event_bits[] values are set back to zero before loading
2118      another value xx_event_bits[x] (each value representing 32 flags)) */
2119
2120   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2121     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2122       change->has_event[i] = TRUE;
2123 }
2124
2125 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2126 {
2127   int i;
2128
2129   /* in contrast to the above function setEventFlagsFromEventBits(), it
2130      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2131      depending on the corresponding change->has_event[i] values here, as
2132      all xx_event_bits[] values are reset in resetEventBits() before */
2133
2134   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2135     if (change->has_event[i])
2136       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2137 }
2138
2139 static char *getDefaultElementDescription(struct ElementInfo *ei)
2140 {
2141   static char description[MAX_ELEMENT_NAME_LEN + 1];
2142   char *default_description = (ei->custom_description != NULL ?
2143                                ei->custom_description :
2144                                ei->editor_description);
2145   int i;
2146
2147   // always start with reliable default values
2148   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2149     description[i] = '\0';
2150
2151   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2152   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2153
2154   return &description[0];
2155 }
2156
2157 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2158 {
2159   char *default_description = getDefaultElementDescription(ei);
2160   int i;
2161
2162   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2163     ei->description[i] = default_description[i];
2164 }
2165
2166 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2167 {
2168   int i;
2169
2170   for (i = 0; conf[i].data_type != -1; i++)
2171   {
2172     int default_value = conf[i].default_value;
2173     int data_type = conf[i].data_type;
2174     int conf_type = conf[i].conf_type;
2175     int byte_mask = conf_type & CONF_MASK_BYTES;
2176
2177     if (byte_mask == CONF_MASK_MULTI_BYTES)
2178     {
2179       int default_num_entities = conf[i].default_num_entities;
2180       int max_num_entities = conf[i].max_num_entities;
2181
2182       *(int *)(conf[i].num_entities) = default_num_entities;
2183
2184       if (data_type == TYPE_STRING)
2185       {
2186         char *default_string = conf[i].default_string;
2187         char *string = (char *)(conf[i].value);
2188
2189         strncpy(string, default_string, max_num_entities);
2190       }
2191       else if (data_type == TYPE_ELEMENT_LIST)
2192       {
2193         int *element_array = (int *)(conf[i].value);
2194         int j;
2195
2196         for (j = 0; j < max_num_entities; j++)
2197           element_array[j] = default_value;
2198       }
2199       else if (data_type == TYPE_CONTENT_LIST)
2200       {
2201         struct Content *content = (struct Content *)(conf[i].value);
2202         int c, x, y;
2203
2204         for (c = 0; c < max_num_entities; c++)
2205           for (y = 0; y < 3; y++)
2206             for (x = 0; x < 3; x++)
2207               content[c].e[x][y] = default_value;
2208       }
2209     }
2210     else        // constant size configuration data (1, 2 or 4 bytes)
2211     {
2212       if (data_type == TYPE_BOOLEAN)
2213         *(boolean *)(conf[i].value) = default_value;
2214       else
2215         *(int *)    (conf[i].value) = default_value;
2216     }
2217   }
2218 }
2219
2220 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2221 {
2222   int i;
2223
2224   for (i = 0; conf[i].data_type != -1; i++)
2225   {
2226     int data_type = conf[i].data_type;
2227     int conf_type = conf[i].conf_type;
2228     int byte_mask = conf_type & CONF_MASK_BYTES;
2229
2230     if (byte_mask == CONF_MASK_MULTI_BYTES)
2231     {
2232       int max_num_entities = conf[i].max_num_entities;
2233
2234       if (data_type == TYPE_STRING)
2235       {
2236         char *string      = (char *)(conf[i].value);
2237         char *string_copy = (char *)(conf[i].value_copy);
2238
2239         strncpy(string_copy, string, max_num_entities);
2240       }
2241       else if (data_type == TYPE_ELEMENT_LIST)
2242       {
2243         int *element_array      = (int *)(conf[i].value);
2244         int *element_array_copy = (int *)(conf[i].value_copy);
2245         int j;
2246
2247         for (j = 0; j < max_num_entities; j++)
2248           element_array_copy[j] = element_array[j];
2249       }
2250       else if (data_type == TYPE_CONTENT_LIST)
2251       {
2252         struct Content *content      = (struct Content *)(conf[i].value);
2253         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2254         int c, x, y;
2255
2256         for (c = 0; c < max_num_entities; c++)
2257           for (y = 0; y < 3; y++)
2258             for (x = 0; x < 3; x++)
2259               content_copy[c].e[x][y] = content[c].e[x][y];
2260       }
2261     }
2262     else        // constant size configuration data (1, 2 or 4 bytes)
2263     {
2264       if (data_type == TYPE_BOOLEAN)
2265         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2266       else
2267         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2268     }
2269   }
2270 }
2271
2272 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2273 {
2274   int i;
2275
2276   xx_ei = *ei_from;     // copy element data into temporary buffer
2277   yy_ei = *ei_to;       // copy element data into temporary buffer
2278
2279   copyConfigFromConfigList(chunk_config_CUSX_base);
2280
2281   *ei_from = xx_ei;
2282   *ei_to   = yy_ei;
2283
2284   // ---------- reinitialize and copy change pages ----------
2285
2286   ei_to->num_change_pages = ei_from->num_change_pages;
2287   ei_to->current_change_page = ei_from->current_change_page;
2288
2289   setElementChangePages(ei_to, ei_to->num_change_pages);
2290
2291   for (i = 0; i < ei_to->num_change_pages; i++)
2292     ei_to->change_page[i] = ei_from->change_page[i];
2293
2294   // ---------- copy group element info ----------
2295   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2296     *ei_to->group = *ei_from->group;
2297
2298   // mark this custom element as modified
2299   ei_to->modified_settings = TRUE;
2300 }
2301
2302 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2303 {
2304   int change_page_size = sizeof(struct ElementChangeInfo);
2305
2306   ei->num_change_pages = MAX(1, change_pages);
2307
2308   ei->change_page =
2309     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2310
2311   if (ei->current_change_page >= ei->num_change_pages)
2312     ei->current_change_page = ei->num_change_pages - 1;
2313
2314   ei->change = &ei->change_page[ei->current_change_page];
2315 }
2316
2317 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2318 {
2319   xx_change = *change;          // copy change data into temporary buffer
2320
2321   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2322
2323   *change = xx_change;
2324
2325   resetEventFlags(change);
2326
2327   change->direct_action = 0;
2328   change->other_action = 0;
2329
2330   change->pre_change_function = NULL;
2331   change->change_function = NULL;
2332   change->post_change_function = NULL;
2333 }
2334
2335 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2336 {
2337   boolean add_border = FALSE;
2338   int x1 = 0;
2339   int y1 = 0;
2340   int x2 = STD_LEV_FIELDX - 1;
2341   int y2 = STD_LEV_FIELDY - 1;
2342   int i, x, y;
2343
2344   li = *level;          // copy level data into temporary buffer
2345   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2346   *level = li;          // copy temporary buffer back to level data
2347
2348   setLevelInfoToDefaults_BD();
2349   setLevelInfoToDefaults_EM();
2350   setLevelInfoToDefaults_SP();
2351   setLevelInfoToDefaults_MM();
2352
2353   level->native_bd_level = &native_bd_level;
2354   level->native_em_level = &native_em_level;
2355   level->native_sp_level = &native_sp_level;
2356   level->native_mm_level = &native_mm_level;
2357
2358   level->file_version = FILE_VERSION_ACTUAL;
2359   level->game_version = GAME_VERSION_ACTUAL;
2360
2361   level->creation_date = getCurrentDate();
2362
2363   level->encoding_16bit_field  = TRUE;
2364   level->encoding_16bit_yamyam = TRUE;
2365   level->encoding_16bit_amoeba = TRUE;
2366
2367   // clear level name and level author string buffers
2368   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2369     level->name[i] = '\0';
2370   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2371     level->author[i] = '\0';
2372
2373   // set level name and level author to default values
2374   strcpy(level->name, NAMELESS_LEVEL_NAME);
2375   strcpy(level->author, ANONYMOUS_NAME);
2376
2377   // set default game engine type
2378   level->game_engine_type = setup.default_game_engine_type;
2379
2380   // some game engines should have a default playfield with border elements
2381   if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2382       level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2383       level->game_engine_type == GAME_ENGINE_TYPE_SP)
2384   {
2385     add_border = TRUE;
2386     x1++;
2387     y1++;
2388     x2--;
2389     y2--;
2390   }
2391
2392   // set level playfield to playable default level with player and exit
2393   for (x = 0; x < MAX_LEV_FIELDX; x++)
2394   {
2395     for (y = 0; y < MAX_LEV_FIELDY; y++)
2396     {
2397       if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2398                          y == 0 || y == STD_LEV_FIELDY - 1))
2399         level->field[x][y] = getEngineElement(EL_STEELWALL);
2400       else
2401         level->field[x][y] = getEngineElement(EL_SAND);
2402     }
2403   }
2404
2405   level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2406   level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2407
2408   BorderElement = getEngineElement(EL_STEELWALL);
2409
2410   // detect custom elements when loading them
2411   level->file_has_custom_elements = FALSE;
2412
2413   // set random colors for BD style levels according to preferred color type
2414   SetRandomLevelColors_BD(setup.bd_default_color_type);
2415
2416   // set default color type and colors for BD style level colors
2417   SetDefaultLevelColorType_BD();
2418   SetDefaultLevelColors_BD();
2419
2420   // set all bug compatibility flags to "false" => do not emulate this bug
2421   level->use_action_after_change_bug = FALSE;
2422
2423   if (leveldir_current)
2424   {
2425     // try to determine better author name than 'anonymous'
2426     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2427     {
2428       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2429       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2430     }
2431     else
2432     {
2433       switch (LEVELCLASS(leveldir_current))
2434       {
2435         case LEVELCLASS_TUTORIAL:
2436           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2437           break;
2438
2439         case LEVELCLASS_CONTRIB:
2440           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2441           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2442           break;
2443
2444         case LEVELCLASS_PRIVATE:
2445           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2446           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2447           break;
2448
2449         default:
2450           // keep default value
2451           break;
2452       }
2453     }
2454   }
2455 }
2456
2457 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2458 {
2459   static boolean clipboard_elements_initialized = FALSE;
2460   int i;
2461
2462   InitElementPropertiesStatic();
2463
2464   li = *level;          // copy level data into temporary buffer
2465   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2466   *level = li;          // copy temporary buffer back to level data
2467
2468   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2469   {
2470     int element = i;
2471     struct ElementInfo *ei = &element_info[element];
2472
2473     if (element == EL_MM_GRAY_BALL)
2474     {
2475       struct LevelInfo_MM *level_mm = level->native_mm_level;
2476       int j;
2477
2478       for (j = 0; j < level->num_mm_ball_contents; j++)
2479         level->mm_ball_content[j] =
2480           map_element_MM_to_RND(level_mm->ball_content[j]);
2481     }
2482
2483     // never initialize clipboard elements after the very first time
2484     // (to be able to use clipboard elements between several levels)
2485     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2486       continue;
2487
2488     if (IS_ENVELOPE(element))
2489     {
2490       int envelope_nr = element - EL_ENVELOPE_1;
2491
2492       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2493
2494       level->envelope[envelope_nr] = xx_envelope;
2495     }
2496
2497     if (IS_CUSTOM_ELEMENT(element) ||
2498         IS_GROUP_ELEMENT(element) ||
2499         IS_INTERNAL_ELEMENT(element))
2500     {
2501       xx_ei = *ei;      // copy element data into temporary buffer
2502
2503       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2504
2505       *ei = xx_ei;
2506     }
2507
2508     setElementChangePages(ei, 1);
2509     setElementChangeInfoToDefaults(ei->change);
2510
2511     if (IS_CUSTOM_ELEMENT(element) ||
2512         IS_GROUP_ELEMENT(element))
2513     {
2514       setElementDescriptionToDefault(ei);
2515
2516       ei->modified_settings = FALSE;
2517     }
2518
2519     if (IS_CUSTOM_ELEMENT(element) ||
2520         IS_INTERNAL_ELEMENT(element))
2521     {
2522       // internal values used in level editor
2523
2524       ei->access_type = 0;
2525       ei->access_layer = 0;
2526       ei->access_protected = 0;
2527       ei->walk_to_action = 0;
2528       ei->smash_targets = 0;
2529       ei->deadliness = 0;
2530
2531       ei->can_explode_by_fire = FALSE;
2532       ei->can_explode_smashed = FALSE;
2533       ei->can_explode_impact = FALSE;
2534
2535       ei->current_change_page = 0;
2536     }
2537
2538     if (IS_GROUP_ELEMENT(element) ||
2539         IS_INTERNAL_ELEMENT(element))
2540     {
2541       struct ElementGroupInfo *group;
2542
2543       // initialize memory for list of elements in group
2544       if (ei->group == NULL)
2545         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2546
2547       group = ei->group;
2548
2549       xx_group = *group;        // copy group data into temporary buffer
2550
2551       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2552
2553       *group = xx_group;
2554     }
2555
2556     if (IS_EMPTY_ELEMENT(element) ||
2557         IS_INTERNAL_ELEMENT(element))
2558     {
2559       xx_ei = *ei;              // copy element data into temporary buffer
2560
2561       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2562
2563       *ei = xx_ei;
2564     }
2565   }
2566
2567   clipboard_elements_initialized = TRUE;
2568 }
2569
2570 static void setLevelInfoToDefaults(struct LevelInfo *level,
2571                                    boolean level_info_only,
2572                                    boolean reset_file_status)
2573 {
2574   setLevelInfoToDefaults_Level(level);
2575
2576   if (!level_info_only)
2577     setLevelInfoToDefaults_Elements(level);
2578
2579   if (reset_file_status)
2580   {
2581     level->no_valid_file = FALSE;
2582     level->no_level_file = FALSE;
2583   }
2584
2585   level->changed = FALSE;
2586 }
2587
2588 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2589 {
2590   level_file_info->nr = 0;
2591   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2592   level_file_info->packed = FALSE;
2593
2594   setString(&level_file_info->basename, NULL);
2595   setString(&level_file_info->filename, NULL);
2596 }
2597
2598 int getMappedElement_SB(int, boolean);
2599
2600 static void ActivateLevelTemplate(void)
2601 {
2602   int x, y;
2603
2604   if (check_special_flags("load_xsb_to_ces"))
2605   {
2606     // fill smaller playfields with padding "beyond border wall" elements
2607     if (level.fieldx < level_template.fieldx ||
2608         level.fieldy < level_template.fieldy)
2609     {
2610       short field[level.fieldx][level.fieldy];
2611       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2612       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2613       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2614       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2615
2616       // copy old playfield (which is smaller than the visible area)
2617       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2618         field[x][y] = level.field[x][y];
2619
2620       // fill new, larger playfield with "beyond border wall" elements
2621       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2622         level.field[x][y] = getMappedElement_SB('_', TRUE);
2623
2624       // copy the old playfield to the middle of the new playfield
2625       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2626         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2627
2628       level.fieldx = new_fieldx;
2629       level.fieldy = new_fieldy;
2630     }
2631   }
2632
2633   // Currently there is no special action needed to activate the template
2634   // data, because 'element_info' property settings overwrite the original
2635   // level data, while all other variables do not change.
2636
2637   // Exception: 'from_level_template' elements in the original level playfield
2638   // are overwritten with the corresponding elements at the same position in
2639   // playfield from the level template.
2640
2641   for (x = 0; x < level.fieldx; x++)
2642     for (y = 0; y < level.fieldy; y++)
2643       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2644         level.field[x][y] = level_template.field[x][y];
2645
2646   if (check_special_flags("load_xsb_to_ces"))
2647   {
2648     struct LevelInfo level_backup = level;
2649
2650     // overwrite all individual level settings from template level settings
2651     level = level_template;
2652
2653     // restore level file info
2654     level.file_info = level_backup.file_info;
2655
2656     // restore playfield size
2657     level.fieldx = level_backup.fieldx;
2658     level.fieldy = level_backup.fieldy;
2659
2660     // restore playfield content
2661     for (x = 0; x < level.fieldx; x++)
2662       for (y = 0; y < level.fieldy; y++)
2663         level.field[x][y] = level_backup.field[x][y];
2664
2665     // restore name and author from individual level
2666     strcpy(level.name,   level_backup.name);
2667     strcpy(level.author, level_backup.author);
2668
2669     // restore flag "use_custom_template"
2670     level.use_custom_template = level_backup.use_custom_template;
2671   }
2672 }
2673
2674 static boolean checkForPackageFromBasename_BD(char *basename)
2675 {
2676   // check for native BD level file extensions
2677   if (!strSuffixLower(basename, ".bd") &&
2678       !strSuffixLower(basename, ".bdr") &&
2679       !strSuffixLower(basename, ".brc") &&
2680       !strSuffixLower(basename, ".gds"))
2681     return FALSE;
2682
2683   // check for standard single-level BD files (like "001.bd")
2684   if (strSuffixLower(basename, ".bd") &&
2685       strlen(basename) == 6 &&
2686       basename[0] >= '0' && basename[0] <= '9' &&
2687       basename[1] >= '0' && basename[1] <= '9' &&
2688       basename[2] >= '0' && basename[2] <= '9')
2689     return FALSE;
2690
2691   // this is a level package in native BD file format
2692   return TRUE;
2693 }
2694
2695 static char *getLevelFilenameFromBasename(char *basename)
2696 {
2697   static char *filename = NULL;
2698
2699   checked_free(filename);
2700
2701   filename = getPath2(getCurrentLevelDir(), basename);
2702
2703   return filename;
2704 }
2705
2706 static int getFileTypeFromBasename(char *basename)
2707 {
2708   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2709
2710   static char *filename = NULL;
2711   struct stat file_status;
2712
2713   // ---------- try to determine file type from filename ----------
2714
2715   // check for typical filename of a Supaplex level package file
2716   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2717     return LEVEL_FILE_TYPE_SP;
2718
2719   // check for typical filename of a Diamond Caves II level package file
2720   if (strSuffixLower(basename, ".dc") ||
2721       strSuffixLower(basename, ".dc2"))
2722     return LEVEL_FILE_TYPE_DC;
2723
2724   // check for typical filename of a Sokoban level package file
2725   if (strSuffixLower(basename, ".xsb") &&
2726       strchr(basename, '%') == NULL)
2727     return LEVEL_FILE_TYPE_SB;
2728
2729   // check for typical filename of a Boulder Dash (GDash) level package file
2730   if (checkForPackageFromBasename_BD(basename))
2731     return LEVEL_FILE_TYPE_BD;
2732
2733   // ---------- try to determine file type from filesize ----------
2734
2735   checked_free(filename);
2736   filename = getPath2(getCurrentLevelDir(), basename);
2737
2738   if (stat(filename, &file_status) == 0)
2739   {
2740     // check for typical filesize of a Supaplex level package file
2741     if (file_status.st_size == 170496)
2742       return LEVEL_FILE_TYPE_SP;
2743   }
2744
2745   return LEVEL_FILE_TYPE_UNKNOWN;
2746 }
2747
2748 static int getFileTypeFromMagicBytes(char *filename, int type)
2749 {
2750   File *file;
2751
2752   if ((file = openFile(filename, MODE_READ)))
2753   {
2754     char chunk_name[CHUNK_ID_LEN + 1];
2755
2756     getFileChunkBE(file, chunk_name, NULL);
2757
2758     if (strEqual(chunk_name, "MMII") ||
2759         strEqual(chunk_name, "MIRR"))
2760       type = LEVEL_FILE_TYPE_MM;
2761
2762     closeFile(file);
2763   }
2764
2765   return type;
2766 }
2767
2768 static boolean checkForPackageFromBasename(char *basename)
2769 {
2770   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2771   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2772
2773   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2774 }
2775
2776 static char *getSingleLevelBasenameExt(int nr, char *extension)
2777 {
2778   static char basename[MAX_FILENAME_LEN];
2779
2780   if (nr < 0)
2781     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2782   else
2783     sprintf(basename, "%03d.%s", nr, extension);
2784
2785   return basename;
2786 }
2787
2788 static char *getSingleLevelBasename(int nr)
2789 {
2790   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2791 }
2792
2793 static char *getPackedLevelBasename(int type)
2794 {
2795   static char basename[MAX_FILENAME_LEN];
2796   char *directory = getCurrentLevelDir();
2797   Directory *dir;
2798   DirectoryEntry *dir_entry;
2799
2800   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2801
2802   if ((dir = openDirectory(directory)) == NULL)
2803   {
2804     Warn("cannot read current level directory '%s'", directory);
2805
2806     return basename;
2807   }
2808
2809   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2810   {
2811     char *entry_basename = dir_entry->basename;
2812     int entry_type = getFileTypeFromBasename(entry_basename);
2813
2814     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2815     {
2816       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2817           type == entry_type)
2818       {
2819         strcpy(basename, entry_basename);
2820
2821         break;
2822       }
2823     }
2824   }
2825
2826   closeDirectory(dir);
2827
2828   return basename;
2829 }
2830
2831 static char *getSingleLevelFilename(int nr)
2832 {
2833   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2834 }
2835
2836 #if ENABLE_UNUSED_CODE
2837 static char *getPackedLevelFilename(int type)
2838 {
2839   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2840 }
2841 #endif
2842
2843 char *getDefaultLevelFilename(int nr)
2844 {
2845   return getSingleLevelFilename(nr);
2846 }
2847
2848 #if ENABLE_UNUSED_CODE
2849 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2850                                                  int type)
2851 {
2852   lfi->type = type;
2853   lfi->packed = FALSE;
2854
2855   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2856   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2857 }
2858 #endif
2859
2860 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2861                                                  int type, char *format, ...)
2862 {
2863   static char basename[MAX_FILENAME_LEN];
2864   va_list ap;
2865
2866   va_start(ap, format);
2867   vsprintf(basename, format, ap);
2868   va_end(ap);
2869
2870   lfi->type = type;
2871   lfi->packed = FALSE;
2872
2873   setString(&lfi->basename, basename);
2874   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2875 }
2876
2877 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2878                                                  int type)
2879 {
2880   lfi->type = type;
2881   lfi->packed = TRUE;
2882
2883   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2884   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2885 }
2886
2887 static int getFiletypeFromID(char *filetype_id)
2888 {
2889   char *filetype_id_lower;
2890   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2891   int i;
2892
2893   if (filetype_id == NULL)
2894     return LEVEL_FILE_TYPE_UNKNOWN;
2895
2896   filetype_id_lower = getStringToLower(filetype_id);
2897
2898   for (i = 0; filetype_id_list[i].id != NULL; i++)
2899   {
2900     char *id_lower = getStringToLower(filetype_id_list[i].id);
2901     
2902     if (strEqual(filetype_id_lower, id_lower))
2903       filetype = filetype_id_list[i].filetype;
2904
2905     free(id_lower);
2906
2907     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2908       break;
2909   }
2910
2911   free(filetype_id_lower);
2912
2913   return filetype;
2914 }
2915
2916 char *getLocalLevelTemplateFilename(void)
2917 {
2918   return getDefaultLevelFilename(-1);
2919 }
2920
2921 char *getGlobalLevelTemplateFilename(void)
2922 {
2923   // global variable "leveldir_current" must be modified in the loop below
2924   LevelDirTree *leveldir_current_last = leveldir_current;
2925   char *filename = NULL;
2926
2927   // check for template level in path from current to topmost tree node
2928
2929   while (leveldir_current != NULL)
2930   {
2931     filename = getDefaultLevelFilename(-1);
2932
2933     if (fileExists(filename))
2934       break;
2935
2936     leveldir_current = leveldir_current->node_parent;
2937   }
2938
2939   // restore global variable "leveldir_current" modified in above loop
2940   leveldir_current = leveldir_current_last;
2941
2942   return filename;
2943 }
2944
2945 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2946 {
2947   int nr = lfi->nr;
2948
2949   // special case: level number is negative => check for level template file
2950   if (nr < 0)
2951   {
2952     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2953                                          getSingleLevelBasename(-1));
2954
2955     // replace local level template filename with global template filename
2956     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2957
2958     // no fallback if template file not existing
2959     return;
2960   }
2961
2962   // special case: check for file name/pattern specified in "levelinfo.conf"
2963   if (leveldir_current->level_filename != NULL)
2964   {
2965     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2966
2967     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2968                                          leveldir_current->level_filename, nr);
2969
2970     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2971
2972     if (fileExists(lfi->filename))
2973       return;
2974   }
2975   else if (leveldir_current->level_filetype != NULL)
2976   {
2977     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2978
2979     // check for specified native level file with standard file name
2980     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2981                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2982     if (fileExists(lfi->filename))
2983       return;
2984   }
2985
2986   // check for native Rocks'n'Diamonds level file
2987   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2988                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2989   if (fileExists(lfi->filename))
2990     return;
2991
2992   // check for native Boulder Dash level file
2993   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2994   if (fileExists(lfi->filename))
2995     return;
2996
2997   // check for Emerald Mine level file (V1)
2998   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2999                                        'a' + (nr / 10) % 26, '0' + nr % 10);
3000   if (fileExists(lfi->filename))
3001     return;
3002   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
3003                                        'A' + (nr / 10) % 26, '0' + nr % 10);
3004   if (fileExists(lfi->filename))
3005     return;
3006
3007   // check for Emerald Mine level file (V2 to V5)
3008   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
3009   if (fileExists(lfi->filename))
3010     return;
3011
3012   // check for Emerald Mine level file (V6 / single mode)
3013   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3014   if (fileExists(lfi->filename))
3015     return;
3016   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3017   if (fileExists(lfi->filename))
3018     return;
3019
3020   // check for Emerald Mine level file (V6 / teamwork mode)
3021   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3022   if (fileExists(lfi->filename))
3023     return;
3024   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3025   if (fileExists(lfi->filename))
3026     return;
3027
3028   // check for various packed level file formats
3029   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3030   if (fileExists(lfi->filename))
3031     return;
3032
3033   // no known level file found -- use default values (and fail later)
3034   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3035                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
3036 }
3037
3038 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3039 {
3040   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3041     lfi->type = getFileTypeFromBasename(lfi->basename);
3042
3043   if (lfi->type == LEVEL_FILE_TYPE_RND)
3044     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3045 }
3046
3047 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3048 {
3049   // always start with reliable default values
3050   setFileInfoToDefaults(level_file_info);
3051
3052   level_file_info->nr = nr;     // set requested level number
3053
3054   determineLevelFileInfo_Filename(level_file_info);
3055   determineLevelFileInfo_Filetype(level_file_info);
3056 }
3057
3058 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3059                               struct LevelFileInfo *lfi_to)
3060 {
3061   lfi_to->nr     = lfi_from->nr;
3062   lfi_to->type   = lfi_from->type;
3063   lfi_to->packed = lfi_from->packed;
3064
3065   setString(&lfi_to->basename, lfi_from->basename);
3066   setString(&lfi_to->filename, lfi_from->filename);
3067 }
3068
3069 // ----------------------------------------------------------------------------
3070 // functions for loading R'n'D level
3071 // ----------------------------------------------------------------------------
3072
3073 int getMappedElement(int element)
3074 {
3075   // remap some (historic, now obsolete) elements
3076
3077   switch (element)
3078   {
3079     case EL_PLAYER_OBSOLETE:
3080       element = EL_PLAYER_1;
3081       break;
3082
3083     case EL_KEY_OBSOLETE:
3084       element = EL_KEY_1;
3085       break;
3086
3087     case EL_EM_KEY_1_FILE_OBSOLETE:
3088       element = EL_EM_KEY_1;
3089       break;
3090
3091     case EL_EM_KEY_2_FILE_OBSOLETE:
3092       element = EL_EM_KEY_2;
3093       break;
3094
3095     case EL_EM_KEY_3_FILE_OBSOLETE:
3096       element = EL_EM_KEY_3;
3097       break;
3098
3099     case EL_EM_KEY_4_FILE_OBSOLETE:
3100       element = EL_EM_KEY_4;
3101       break;
3102
3103     case EL_ENVELOPE_OBSOLETE:
3104       element = EL_ENVELOPE_1;
3105       break;
3106
3107     case EL_SP_EMPTY:
3108       element = EL_EMPTY;
3109       break;
3110
3111     default:
3112       if (element >= NUM_FILE_ELEMENTS)
3113       {
3114         Warn("invalid level element %d", element);
3115
3116         element = EL_UNKNOWN;
3117       }
3118       break;
3119   }
3120
3121   return element;
3122 }
3123
3124 static int getMappedElementByVersion(int element, int game_version)
3125 {
3126   // remap some elements due to certain game version
3127
3128   if (game_version <= VERSION_IDENT(2,2,0,0))
3129   {
3130     // map game font elements
3131     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3132                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3133                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3134                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3135   }
3136
3137   if (game_version < VERSION_IDENT(3,0,0,0))
3138   {
3139     // map Supaplex gravity tube elements
3140     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3141                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3142                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3143                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3144                element);
3145   }
3146
3147   return element;
3148 }
3149
3150 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3151 {
3152   level->file_version = getFileVersion(file);
3153   level->game_version = getFileVersion(file);
3154
3155   return chunk_size;
3156 }
3157
3158 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3159 {
3160   level->creation_date.year  = getFile16BitBE(file);
3161   level->creation_date.month = getFile8Bit(file);
3162   level->creation_date.day   = getFile8Bit(file);
3163
3164   level->creation_date.src   = DATE_SRC_LEVELFILE;
3165
3166   return chunk_size;
3167 }
3168
3169 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3170 {
3171   int initial_player_stepsize;
3172   int initial_player_gravity;
3173   int i, x, y;
3174
3175   level->fieldx = getFile8Bit(file);
3176   level->fieldy = getFile8Bit(file);
3177
3178   level->time           = getFile16BitBE(file);
3179   level->gems_needed    = getFile16BitBE(file);
3180
3181   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3182     level->name[i] = getFile8Bit(file);
3183   level->name[MAX_LEVEL_NAME_LEN] = 0;
3184
3185   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3186     level->score[i] = getFile8Bit(file);
3187
3188   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3189   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3190     for (y = 0; y < 3; y++)
3191       for (x = 0; x < 3; x++)
3192         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3193
3194   level->amoeba_speed           = getFile8Bit(file);
3195   level->time_magic_wall        = getFile8Bit(file);
3196   level->time_wheel             = getFile8Bit(file);
3197   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3198
3199   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3200                                    STEPSIZE_NORMAL);
3201
3202   for (i = 0; i < MAX_PLAYERS; i++)
3203     level->initial_player_stepsize[i] = initial_player_stepsize;
3204
3205   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3206
3207   for (i = 0; i < MAX_PLAYERS; i++)
3208     level->initial_player_gravity[i] = initial_player_gravity;
3209
3210   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3211   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3212
3213   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3214
3215   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3216   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3217   level->can_move_into_acid_bits = getFile32BitBE(file);
3218   level->dont_collide_with_bits = getFile8Bit(file);
3219
3220   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3221   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3222
3223   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3224   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3225   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3226
3227   level->game_engine_type       = getFile8Bit(file);
3228
3229   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3230
3231   return chunk_size;
3232 }
3233
3234 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3235 {
3236   int i;
3237
3238   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3239     level->name[i] = getFile8Bit(file);
3240   level->name[MAX_LEVEL_NAME_LEN] = 0;
3241
3242   return chunk_size;
3243 }
3244
3245 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3246 {
3247   int i;
3248
3249   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3250     level->author[i] = getFile8Bit(file);
3251   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3252
3253   return chunk_size;
3254 }
3255
3256 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3257 {
3258   int x, y;
3259   int chunk_size_expected = level->fieldx * level->fieldy;
3260
3261   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3262      stored with 16-bit encoding (and should be twice as big then).
3263      Even worse, playfield data was stored 16-bit when only yamyam content
3264      contained 16-bit elements and vice versa. */
3265
3266   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3267     chunk_size_expected *= 2;
3268
3269   if (chunk_size_expected != chunk_size)
3270   {
3271     ReadUnusedBytesFromFile(file, chunk_size);
3272     return chunk_size_expected;
3273   }
3274
3275   for (y = 0; y < level->fieldy; y++)
3276     for (x = 0; x < level->fieldx; x++)
3277       level->field[x][y] =
3278         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3279                          getFile8Bit(file));
3280   return chunk_size;
3281 }
3282
3283 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3284 {
3285   int i, x, y;
3286   int header_size = 4;
3287   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3288   int chunk_size_expected = header_size + content_size;
3289
3290   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3291      stored with 16-bit encoding (and should be twice as big then).
3292      Even worse, playfield data was stored 16-bit when only yamyam content
3293      contained 16-bit elements and vice versa. */
3294
3295   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3296     chunk_size_expected += content_size;
3297
3298   if (chunk_size_expected != chunk_size)
3299   {
3300     ReadUnusedBytesFromFile(file, chunk_size);
3301     return chunk_size_expected;
3302   }
3303
3304   getFile8Bit(file);
3305   level->num_yamyam_contents = getFile8Bit(file);
3306   getFile8Bit(file);
3307   getFile8Bit(file);
3308
3309   // correct invalid number of content fields -- should never happen
3310   if (level->num_yamyam_contents < 1 ||
3311       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3312     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3313
3314   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3315     for (y = 0; y < 3; y++)
3316       for (x = 0; x < 3; x++)
3317         level->yamyam_content[i].e[x][y] =
3318           getMappedElement(level->encoding_16bit_field ?
3319                            getFile16BitBE(file) : getFile8Bit(file));
3320   return chunk_size;
3321 }
3322
3323 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3324 {
3325   int i, x, y;
3326   int element;
3327   int num_contents;
3328   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3329
3330   element = getMappedElement(getFile16BitBE(file));
3331   num_contents = getFile8Bit(file);
3332
3333   getFile8Bit(file);    // content x size (unused)
3334   getFile8Bit(file);    // content y size (unused)
3335
3336   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3337
3338   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3339     for (y = 0; y < 3; y++)
3340       for (x = 0; x < 3; x++)
3341         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3342
3343   // correct invalid number of content fields -- should never happen
3344   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3345     num_contents = STD_ELEMENT_CONTENTS;
3346
3347   if (element == EL_YAMYAM)
3348   {
3349     level->num_yamyam_contents = num_contents;
3350
3351     for (i = 0; i < num_contents; i++)
3352       for (y = 0; y < 3; y++)
3353         for (x = 0; x < 3; x++)
3354           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3355   }
3356   else if (element == EL_BD_AMOEBA)
3357   {
3358     level->amoeba_content = content_array[0][0][0];
3359   }
3360   else
3361   {
3362     Warn("cannot load content for element '%d'", element);
3363   }
3364
3365   return chunk_size;
3366 }
3367
3368 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3369 {
3370   int i;
3371   int element;
3372   int envelope_nr;
3373   int envelope_len;
3374   int chunk_size_expected;
3375
3376   element = getMappedElement(getFile16BitBE(file));
3377   if (!IS_ENVELOPE(element))
3378     element = EL_ENVELOPE_1;
3379
3380   envelope_nr = element - EL_ENVELOPE_1;
3381
3382   envelope_len = getFile16BitBE(file);
3383
3384   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3385   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3386
3387   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3388
3389   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3390   if (chunk_size_expected != chunk_size)
3391   {
3392     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3393     return chunk_size_expected;
3394   }
3395
3396   for (i = 0; i < envelope_len; i++)
3397     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3398
3399   return chunk_size;
3400 }
3401
3402 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3403 {
3404   int num_changed_custom_elements = getFile16BitBE(file);
3405   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3406   int i;
3407
3408   if (chunk_size_expected != chunk_size)
3409   {
3410     ReadUnusedBytesFromFile(file, chunk_size - 2);
3411     return chunk_size_expected;
3412   }
3413
3414   for (i = 0; i < num_changed_custom_elements; i++)
3415   {
3416     int element = getMappedElement(getFile16BitBE(file));
3417     int properties = getFile32BitBE(file);
3418
3419     if (IS_CUSTOM_ELEMENT(element))
3420       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3421     else
3422       Warn("invalid custom element number %d", element);
3423
3424     // older game versions that wrote level files with CUS1 chunks used
3425     // different default push delay values (not yet stored in level file)
3426     element_info[element].push_delay_fixed = 2;
3427     element_info[element].push_delay_random = 8;
3428   }
3429
3430   level->file_has_custom_elements = TRUE;
3431
3432   return chunk_size;
3433 }
3434
3435 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3436 {
3437   int num_changed_custom_elements = getFile16BitBE(file);
3438   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3439   int i;
3440
3441   if (chunk_size_expected != chunk_size)
3442   {
3443     ReadUnusedBytesFromFile(file, chunk_size - 2);
3444     return chunk_size_expected;
3445   }
3446
3447   for (i = 0; i < num_changed_custom_elements; i++)
3448   {
3449     int element = getMappedElement(getFile16BitBE(file));
3450     int custom_target_element = getMappedElement(getFile16BitBE(file));
3451
3452     if (IS_CUSTOM_ELEMENT(element))
3453       element_info[element].change->target_element = custom_target_element;
3454     else
3455       Warn("invalid custom element number %d", element);
3456   }
3457
3458   level->file_has_custom_elements = TRUE;
3459
3460   return chunk_size;
3461 }
3462
3463 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3464 {
3465   int num_changed_custom_elements = getFile16BitBE(file);
3466   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3467   int i, j, x, y;
3468
3469   if (chunk_size_expected != chunk_size)
3470   {
3471     ReadUnusedBytesFromFile(file, chunk_size - 2);
3472     return chunk_size_expected;
3473   }
3474
3475   for (i = 0; i < num_changed_custom_elements; i++)
3476   {
3477     int element = getMappedElement(getFile16BitBE(file));
3478     struct ElementInfo *ei = &element_info[element];
3479     unsigned int event_bits;
3480
3481     if (!IS_CUSTOM_ELEMENT(element))
3482     {
3483       Warn("invalid custom element number %d", element);
3484
3485       element = EL_INTERNAL_DUMMY;
3486     }
3487
3488     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3489       ei->description[j] = getFile8Bit(file);
3490     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3491
3492     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3493
3494     // some free bytes for future properties and padding
3495     ReadUnusedBytesFromFile(file, 7);
3496
3497     ei->use_gfx_element = getFile8Bit(file);
3498     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3499
3500     ei->collect_score_initial = getFile8Bit(file);
3501     ei->collect_count_initial = getFile8Bit(file);
3502
3503     ei->push_delay_fixed = getFile16BitBE(file);
3504     ei->push_delay_random = getFile16BitBE(file);
3505     ei->move_delay_fixed = getFile16BitBE(file);
3506     ei->move_delay_random = getFile16BitBE(file);
3507
3508     ei->move_pattern = getFile16BitBE(file);
3509     ei->move_direction_initial = getFile8Bit(file);
3510     ei->move_stepsize = getFile8Bit(file);
3511
3512     for (y = 0; y < 3; y++)
3513       for (x = 0; x < 3; x++)
3514         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3515
3516     // bits 0 - 31 of "has_event[]"
3517     event_bits = getFile32BitBE(file);
3518     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3519       if (event_bits & (1u << j))
3520         ei->change->has_event[j] = TRUE;
3521
3522     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3523
3524     ei->change->delay_fixed = getFile16BitBE(file);
3525     ei->change->delay_random = getFile16BitBE(file);
3526     ei->change->delay_frames = getFile16BitBE(file);
3527
3528     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3529
3530     ei->change->explode = getFile8Bit(file);
3531     ei->change->use_target_content = getFile8Bit(file);
3532     ei->change->only_if_complete = getFile8Bit(file);
3533     ei->change->use_random_replace = getFile8Bit(file);
3534
3535     ei->change->random_percentage = getFile8Bit(file);
3536     ei->change->replace_when = getFile8Bit(file);
3537
3538     for (y = 0; y < 3; y++)
3539       for (x = 0; x < 3; x++)
3540         ei->change->target_content.e[x][y] =
3541           getMappedElement(getFile16BitBE(file));
3542
3543     ei->slippery_type = getFile8Bit(file);
3544
3545     // some free bytes for future properties and padding
3546     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3547
3548     // mark that this custom element has been modified
3549     ei->modified_settings = TRUE;
3550   }
3551
3552   level->file_has_custom_elements = TRUE;
3553
3554   return chunk_size;
3555 }
3556
3557 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3558 {
3559   struct ElementInfo *ei;
3560   int chunk_size_expected;
3561   int element;
3562   int i, j, x, y;
3563
3564   // ---------- custom element base property values (96 bytes) ----------------
3565
3566   element = getMappedElement(getFile16BitBE(file));
3567
3568   if (!IS_CUSTOM_ELEMENT(element))
3569   {
3570     Warn("invalid custom element number %d", element);
3571
3572     ReadUnusedBytesFromFile(file, chunk_size - 2);
3573
3574     return chunk_size;
3575   }
3576
3577   ei = &element_info[element];
3578
3579   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3580     ei->description[i] = getFile8Bit(file);
3581   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3582
3583   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3584
3585   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3586
3587   ei->num_change_pages = getFile8Bit(file);
3588
3589   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3590   if (chunk_size_expected != chunk_size)
3591   {
3592     ReadUnusedBytesFromFile(file, chunk_size - 43);
3593     return chunk_size_expected;
3594   }
3595
3596   ei->ce_value_fixed_initial = getFile16BitBE(file);
3597   ei->ce_value_random_initial = getFile16BitBE(file);
3598   ei->use_last_ce_value = getFile8Bit(file);
3599
3600   ei->use_gfx_element = getFile8Bit(file);
3601   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3602
3603   ei->collect_score_initial = getFile8Bit(file);
3604   ei->collect_count_initial = getFile8Bit(file);
3605
3606   ei->drop_delay_fixed = getFile8Bit(file);
3607   ei->push_delay_fixed = getFile8Bit(file);
3608   ei->drop_delay_random = getFile8Bit(file);
3609   ei->push_delay_random = getFile8Bit(file);
3610   ei->move_delay_fixed = getFile16BitBE(file);
3611   ei->move_delay_random = getFile16BitBE(file);
3612
3613   // bits 0 - 15 of "move_pattern" ...
3614   ei->move_pattern = getFile16BitBE(file);
3615   ei->move_direction_initial = getFile8Bit(file);
3616   ei->move_stepsize = getFile8Bit(file);
3617
3618   ei->slippery_type = getFile8Bit(file);
3619
3620   for (y = 0; y < 3; y++)
3621     for (x = 0; x < 3; x++)
3622       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3623
3624   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3625   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3626   ei->move_leave_type = getFile8Bit(file);
3627
3628   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3629   ei->move_pattern |= (getFile16BitBE(file) << 16);
3630
3631   ei->access_direction = getFile8Bit(file);
3632
3633   ei->explosion_delay = getFile8Bit(file);
3634   ei->ignition_delay = getFile8Bit(file);
3635   ei->explosion_type = getFile8Bit(file);
3636
3637   // some free bytes for future custom property values and padding
3638   ReadUnusedBytesFromFile(file, 1);
3639
3640   // ---------- change page property values (48 bytes) ------------------------
3641
3642   setElementChangePages(ei, ei->num_change_pages);
3643
3644   for (i = 0; i < ei->num_change_pages; i++)
3645   {
3646     struct ElementChangeInfo *change = &ei->change_page[i];
3647     unsigned int event_bits;
3648
3649     // always start with reliable default values
3650     setElementChangeInfoToDefaults(change);
3651
3652     // bits 0 - 31 of "has_event[]" ...
3653     event_bits = getFile32BitBE(file);
3654     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3655       if (event_bits & (1u << j))
3656         change->has_event[j] = TRUE;
3657
3658     change->target_element = getMappedElement(getFile16BitBE(file));
3659
3660     change->delay_fixed = getFile16BitBE(file);
3661     change->delay_random = getFile16BitBE(file);
3662     change->delay_frames = getFile16BitBE(file);
3663
3664     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3665
3666     change->explode = getFile8Bit(file);
3667     change->use_target_content = getFile8Bit(file);
3668     change->only_if_complete = getFile8Bit(file);
3669     change->use_random_replace = getFile8Bit(file);
3670
3671     change->random_percentage = getFile8Bit(file);
3672     change->replace_when = getFile8Bit(file);
3673
3674     for (y = 0; y < 3; y++)
3675       for (x = 0; x < 3; x++)
3676         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3677
3678     change->can_change = getFile8Bit(file);
3679
3680     change->trigger_side = getFile8Bit(file);
3681
3682     change->trigger_player = getFile8Bit(file);
3683     change->trigger_page = getFile8Bit(file);
3684
3685     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3686                             CH_PAGE_ANY : (1 << change->trigger_page));
3687
3688     change->has_action = getFile8Bit(file);
3689     change->action_type = getFile8Bit(file);
3690     change->action_mode = getFile8Bit(file);
3691     change->action_arg = getFile16BitBE(file);
3692
3693     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3694     event_bits = getFile8Bit(file);
3695     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3696       if (event_bits & (1u << (j - 32)))
3697         change->has_event[j] = TRUE;
3698   }
3699
3700   // mark this custom element as modified
3701   ei->modified_settings = TRUE;
3702
3703   level->file_has_custom_elements = TRUE;
3704
3705   return chunk_size;
3706 }
3707
3708 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3709 {
3710   struct ElementInfo *ei;
3711   struct ElementGroupInfo *group;
3712   int element;
3713   int i;
3714
3715   element = getMappedElement(getFile16BitBE(file));
3716
3717   if (!IS_GROUP_ELEMENT(element))
3718   {
3719     Warn("invalid group element number %d", element);
3720
3721     ReadUnusedBytesFromFile(file, chunk_size - 2);
3722
3723     return chunk_size;
3724   }
3725
3726   ei = &element_info[element];
3727
3728   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3729     ei->description[i] = getFile8Bit(file);
3730   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3731
3732   group = element_info[element].group;
3733
3734   group->num_elements = getFile8Bit(file);
3735
3736   ei->use_gfx_element = getFile8Bit(file);
3737   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3738
3739   group->choice_mode = getFile8Bit(file);
3740
3741   // some free bytes for future values and padding
3742   ReadUnusedBytesFromFile(file, 3);
3743
3744   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3745     group->element[i] = getMappedElement(getFile16BitBE(file));
3746
3747   // mark this group element as modified
3748   element_info[element].modified_settings = TRUE;
3749
3750   level->file_has_custom_elements = TRUE;
3751
3752   return chunk_size;
3753 }
3754
3755 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3756                                 int element, int real_element)
3757 {
3758   int micro_chunk_size = 0;
3759   int conf_type = getFile8Bit(file);
3760   int byte_mask = conf_type & CONF_MASK_BYTES;
3761   boolean element_found = FALSE;
3762   int i;
3763
3764   micro_chunk_size += 1;
3765
3766   if (byte_mask == CONF_MASK_MULTI_BYTES)
3767   {
3768     int num_bytes = getFile16BitBE(file);
3769     byte *buffer = checked_malloc(num_bytes);
3770
3771     ReadBytesFromFile(file, buffer, num_bytes);
3772
3773     for (i = 0; conf[i].data_type != -1; i++)
3774     {
3775       if (conf[i].element == element &&
3776           conf[i].conf_type == conf_type)
3777       {
3778         int data_type = conf[i].data_type;
3779         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3780         int max_num_entities = conf[i].max_num_entities;
3781
3782         if (num_entities > max_num_entities)
3783         {
3784           Warn("truncating number of entities for element %d from %d to %d",
3785                element, num_entities, max_num_entities);
3786
3787           num_entities = max_num_entities;
3788         }
3789
3790         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3791                                   data_type == TYPE_CONTENT_LIST))
3792         {
3793           // for element and content lists, zero entities are not allowed
3794           Warn("found empty list of entities for element %d", element);
3795
3796           // do not set "num_entities" here to prevent reading behind buffer
3797
3798           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3799         }
3800         else
3801         {
3802           *(int *)(conf[i].num_entities) = num_entities;
3803         }
3804
3805         element_found = TRUE;
3806
3807         if (data_type == TYPE_STRING)
3808         {
3809           char *string = (char *)(conf[i].value);
3810           int j;
3811
3812           for (j = 0; j < max_num_entities; j++)
3813             string[j] = (j < num_entities ? buffer[j] : '\0');
3814         }
3815         else if (data_type == TYPE_ELEMENT_LIST)
3816         {
3817           int *element_array = (int *)(conf[i].value);
3818           int j;
3819
3820           for (j = 0; j < num_entities; j++)
3821             element_array[j] =
3822               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3823         }
3824         else if (data_type == TYPE_CONTENT_LIST)
3825         {
3826           struct Content *content= (struct Content *)(conf[i].value);
3827           int c, x, y;
3828
3829           for (c = 0; c < num_entities; c++)
3830             for (y = 0; y < 3; y++)
3831               for (x = 0; x < 3; x++)
3832                 content[c].e[x][y] =
3833                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3834         }
3835         else
3836           element_found = FALSE;
3837
3838         break;
3839       }
3840     }
3841
3842     checked_free(buffer);
3843
3844     micro_chunk_size += 2 + num_bytes;
3845   }
3846   else          // constant size configuration data (1, 2 or 4 bytes)
3847   {
3848     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3849                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3850                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3851
3852     for (i = 0; conf[i].data_type != -1; i++)
3853     {
3854       if (conf[i].element == element &&
3855           conf[i].conf_type == conf_type)
3856       {
3857         int data_type = conf[i].data_type;
3858
3859         if (data_type == TYPE_ELEMENT)
3860           value = getMappedElement(value);
3861
3862         if (data_type == TYPE_BOOLEAN)
3863           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3864         else
3865           *(int *)    (conf[i].value) = value;
3866
3867         element_found = TRUE;
3868
3869         break;
3870       }
3871     }
3872
3873     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3874   }
3875
3876   if (!element_found)
3877   {
3878     char *error_conf_chunk_bytes =
3879       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3880        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3881        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3882     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3883     int error_element = real_element;
3884
3885     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3886          error_conf_chunk_bytes, error_conf_chunk_token,
3887          error_element, EL_NAME(error_element));
3888   }
3889
3890   return micro_chunk_size;
3891 }
3892
3893 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3894 {
3895   int real_chunk_size = 0;
3896
3897   li = *level;          // copy level data into temporary buffer
3898
3899   while (!checkEndOfFile(file))
3900   {
3901     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3902
3903     if (real_chunk_size >= chunk_size)
3904       break;
3905   }
3906
3907   *level = li;          // copy temporary buffer back to level data
3908
3909   return real_chunk_size;
3910 }
3911
3912 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3913 {
3914   int real_chunk_size = 0;
3915
3916   li = *level;          // copy level data into temporary buffer
3917
3918   while (!checkEndOfFile(file))
3919   {
3920     int element = getMappedElement(getFile16BitBE(file));
3921
3922     real_chunk_size += 2;
3923     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3924                                             element, element);
3925     if (real_chunk_size >= chunk_size)
3926       break;
3927   }
3928
3929   *level = li;          // copy temporary buffer back to level data
3930
3931   return real_chunk_size;
3932 }
3933
3934 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3935 {
3936   int real_chunk_size = 0;
3937
3938   li = *level;          // copy level data into temporary buffer
3939
3940   while (!checkEndOfFile(file))
3941   {
3942     int element = getMappedElement(getFile16BitBE(file));
3943
3944     real_chunk_size += 2;
3945     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3946                                             element, element);
3947     if (real_chunk_size >= chunk_size)
3948       break;
3949   }
3950
3951   *level = li;          // copy temporary buffer back to level data
3952
3953   return real_chunk_size;
3954 }
3955
3956 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3957 {
3958   int element = getMappedElement(getFile16BitBE(file));
3959   int envelope_nr = element - EL_ENVELOPE_1;
3960   int real_chunk_size = 2;
3961
3962   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3963
3964   while (!checkEndOfFile(file))
3965   {
3966     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3967                                             -1, element);
3968
3969     if (real_chunk_size >= chunk_size)
3970       break;
3971   }
3972
3973   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3974
3975   return real_chunk_size;
3976 }
3977
3978 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3979 {
3980   int element = getMappedElement(getFile16BitBE(file));
3981   int real_chunk_size = 2;
3982   struct ElementInfo *ei = &element_info[element];
3983   int i;
3984
3985   xx_ei = *ei;          // copy element data into temporary buffer
3986
3987   xx_ei.num_change_pages = -1;
3988
3989   while (!checkEndOfFile(file))
3990   {
3991     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3992                                             -1, element);
3993     if (xx_ei.num_change_pages != -1)
3994       break;
3995
3996     if (real_chunk_size >= chunk_size)
3997       break;
3998   }
3999
4000   *ei = xx_ei;
4001
4002   if (ei->num_change_pages == -1)
4003   {
4004     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4005          EL_NAME(element));
4006
4007     ei->num_change_pages = 1;
4008
4009     setElementChangePages(ei, 1);
4010     setElementChangeInfoToDefaults(ei->change);
4011
4012     return real_chunk_size;
4013   }
4014
4015   // initialize number of change pages stored for this custom element
4016   setElementChangePages(ei, ei->num_change_pages);
4017   for (i = 0; i < ei->num_change_pages; i++)
4018     setElementChangeInfoToDefaults(&ei->change_page[i]);
4019
4020   // start with reading properties for the first change page
4021   xx_current_change_page = 0;
4022
4023   while (!checkEndOfFile(file))
4024   {
4025     // level file might contain invalid change page number
4026     if (xx_current_change_page >= ei->num_change_pages)
4027       break;
4028
4029     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4030
4031     xx_change = *change;        // copy change data into temporary buffer
4032
4033     resetEventBits();           // reset bits; change page might have changed
4034
4035     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4036                                             -1, element);
4037
4038     *change = xx_change;
4039
4040     setEventFlagsFromEventBits(change);
4041
4042     if (real_chunk_size >= chunk_size)
4043       break;
4044   }
4045
4046   level->file_has_custom_elements = TRUE;
4047
4048   return real_chunk_size;
4049 }
4050
4051 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4052 {
4053   int element = getMappedElement(getFile16BitBE(file));
4054   int real_chunk_size = 2;
4055   struct ElementInfo *ei = &element_info[element];
4056   struct ElementGroupInfo *group = ei->group;
4057
4058   if (group == NULL)
4059     return -1;
4060
4061   xx_ei = *ei;          // copy element data into temporary buffer
4062   xx_group = *group;    // copy group data into temporary buffer
4063
4064   while (!checkEndOfFile(file))
4065   {
4066     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4067                                             -1, element);
4068
4069     if (real_chunk_size >= chunk_size)
4070       break;
4071   }
4072
4073   *ei = xx_ei;
4074   *group = xx_group;
4075
4076   level->file_has_custom_elements = TRUE;
4077
4078   return real_chunk_size;
4079 }
4080
4081 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4082 {
4083   int element = getMappedElement(getFile16BitBE(file));
4084   int real_chunk_size = 2;
4085   struct ElementInfo *ei = &element_info[element];
4086
4087   xx_ei = *ei;          // copy element data into temporary buffer
4088
4089   while (!checkEndOfFile(file))
4090   {
4091     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4092                                             -1, element);
4093
4094     if (real_chunk_size >= chunk_size)
4095       break;
4096   }
4097
4098   *ei = xx_ei;
4099
4100   level->file_has_custom_elements = TRUE;
4101
4102   return real_chunk_size;
4103 }
4104
4105 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4106                                       struct LevelFileInfo *level_file_info,
4107                                       boolean level_info_only)
4108 {
4109   char *filename = level_file_info->filename;
4110   char cookie[MAX_LINE_LEN];
4111   char chunk_name[CHUNK_ID_LEN + 1];
4112   int chunk_size;
4113   File *file;
4114
4115   if (!(file = openFile(filename, MODE_READ)))
4116   {
4117     level->no_valid_file = TRUE;
4118     level->no_level_file = TRUE;
4119
4120     if (level_info_only)
4121       return;
4122
4123     Warn("cannot read level '%s' -- using empty level", filename);
4124
4125     if (!setup.editor.use_template_for_new_levels)
4126       return;
4127
4128     // if level file not found, try to initialize level data from template
4129     filename = getGlobalLevelTemplateFilename();
4130
4131     if (!(file = openFile(filename, MODE_READ)))
4132       return;
4133
4134     // default: for empty levels, use level template for custom elements
4135     level->use_custom_template = TRUE;
4136
4137     level->no_valid_file = FALSE;
4138   }
4139
4140   getFileChunkBE(file, chunk_name, NULL);
4141   if (strEqual(chunk_name, "RND1"))
4142   {
4143     getFile32BitBE(file);               // not used
4144
4145     getFileChunkBE(file, chunk_name, NULL);
4146     if (!strEqual(chunk_name, "CAVE"))
4147     {
4148       level->no_valid_file = TRUE;
4149
4150       Warn("unknown format of level file '%s'", filename);
4151
4152       closeFile(file);
4153
4154       return;
4155     }
4156   }
4157   else  // check for pre-2.0 file format with cookie string
4158   {
4159     strcpy(cookie, chunk_name);
4160     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4161       cookie[4] = '\0';
4162     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4163       cookie[strlen(cookie) - 1] = '\0';
4164
4165     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4166     {
4167       level->no_valid_file = TRUE;
4168
4169       Warn("unknown format of level file '%s'", filename);
4170
4171       closeFile(file);
4172
4173       return;
4174     }
4175
4176     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4177     {
4178       level->no_valid_file = TRUE;
4179
4180       Warn("unsupported version of level file '%s'", filename);
4181
4182       closeFile(file);
4183
4184       return;
4185     }
4186
4187     // pre-2.0 level files have no game version, so use file version here
4188     level->game_version = level->file_version;
4189   }
4190
4191   if (level->file_version < FILE_VERSION_1_2)
4192   {
4193     // level files from versions before 1.2.0 without chunk structure
4194     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4195     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4196   }
4197   else
4198   {
4199     static struct
4200     {
4201       char *name;
4202       int size;
4203       int (*loader)(File *, int, struct LevelInfo *);
4204     }
4205     chunk_info[] =
4206     {
4207       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4208       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4209       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4210       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4211       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4212       { "INFO", -1,                     LoadLevel_INFO },
4213       { "BODY", -1,                     LoadLevel_BODY },
4214       { "CONT", -1,                     LoadLevel_CONT },
4215       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4216       { "CNT3", -1,                     LoadLevel_CNT3 },
4217       { "CUS1", -1,                     LoadLevel_CUS1 },
4218       { "CUS2", -1,                     LoadLevel_CUS2 },
4219       { "CUS3", -1,                     LoadLevel_CUS3 },
4220       { "CUS4", -1,                     LoadLevel_CUS4 },
4221       { "GRP1", -1,                     LoadLevel_GRP1 },
4222       { "CONF", -1,                     LoadLevel_CONF },
4223       { "ELEM", -1,                     LoadLevel_ELEM },
4224       { "NOTE", -1,                     LoadLevel_NOTE },
4225       { "CUSX", -1,                     LoadLevel_CUSX },
4226       { "GRPX", -1,                     LoadLevel_GRPX },
4227       { "EMPX", -1,                     LoadLevel_EMPX },
4228
4229       {  NULL,  0,                      NULL }
4230     };
4231
4232     while (getFileChunkBE(file, chunk_name, &chunk_size))
4233     {
4234       int i = 0;
4235
4236       while (chunk_info[i].name != NULL &&
4237              !strEqual(chunk_name, chunk_info[i].name))
4238         i++;
4239
4240       if (chunk_info[i].name == NULL)
4241       {
4242         Warn("unknown chunk '%s' in level file '%s'",
4243              chunk_name, filename);
4244
4245         ReadUnusedBytesFromFile(file, chunk_size);
4246       }
4247       else if (chunk_info[i].size != -1 &&
4248                chunk_info[i].size != chunk_size)
4249       {
4250         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4251              chunk_size, chunk_name, filename);
4252
4253         ReadUnusedBytesFromFile(file, chunk_size);
4254       }
4255       else
4256       {
4257         // call function to load this level chunk
4258         int chunk_size_expected =
4259           (chunk_info[i].loader)(file, chunk_size, level);
4260
4261         if (chunk_size_expected < 0)
4262         {
4263           Warn("error reading chunk '%s' in level file '%s'",
4264                chunk_name, filename);
4265
4266           break;
4267         }
4268
4269         // the size of some chunks cannot be checked before reading other
4270         // chunks first (like "HEAD" and "BODY") that contain some header
4271         // information, so check them here
4272         if (chunk_size_expected != chunk_size)
4273         {
4274           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4275                chunk_size, chunk_name, filename);
4276
4277           break;
4278         }
4279       }
4280     }
4281   }
4282
4283   closeFile(file);
4284 }
4285
4286
4287 // ----------------------------------------------------------------------------
4288 // functions for loading BD level
4289 // ----------------------------------------------------------------------------
4290
4291 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4292 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4293
4294 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4295 {
4296   struct LevelInfo_BD *level_bd = level->native_bd_level;
4297   GdCave *cave = NULL;  // will be changed below
4298   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4299   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4300   int x, y;
4301
4302   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4303
4304   // cave and map newly allocated when set to defaults above
4305   cave = level_bd->cave;
4306
4307   // level type
4308   cave->intermission                    = level->bd_intermission;
4309
4310   // level settings
4311   cave->level_time[0]                   = level->time;
4312   cave->level_diamonds[0]               = level->gems_needed;
4313
4314   // game timing
4315   cave->scheduling                      = level->bd_scheduling_type;
4316   cave->pal_timing                      = level->bd_pal_timing;
4317   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4318   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4319   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4320   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4321
4322   // scores
4323   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4324   cave->diamond_value                   = level->score[SC_EMERALD];
4325   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4326
4327   // compatibility settings
4328   cave->lineshift                       = level->bd_line_shifting_borders;
4329   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4330   cave->short_explosions                = level->bd_short_explosions;
4331
4332   // player properties
4333   cave->diagonal_movements              = level->bd_diagonal_movements;
4334   cave->active_is_first_found           = level->bd_topmost_player_active;
4335   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4336   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4337   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4338   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4339
4340   // element properties
4341   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4342   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4343   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4344   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4345   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4346   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4347   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4348   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4349   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4350   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4351   cave->magic_wall_breakscan            = level->bd_magic_wall_break_scan;
4352
4353   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4354   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4355   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4356   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4357   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4358   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4359   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4360
4361   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4362   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4363   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4364   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4365   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4366   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4367   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4368   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4369   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4370   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4371   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4372
4373   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4374   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4375   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4376   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4377   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4378   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4379
4380   cave->slime_predictable               = level->bd_slime_is_predictable;
4381   cave->slime_correct_random            = level->bd_slime_correct_random;
4382   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4383   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4384   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4385   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4386   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4387   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4388   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4389   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4390   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4391   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4392
4393   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4394   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4395   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4396
4397   cave->biter_delay_frame               = level->bd_biter_move_delay;
4398   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4399
4400   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4401
4402   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4403
4404   cave->replicators_active              = level->bd_replicators_active;
4405   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4406
4407   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4408   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4409
4410   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4411
4412   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4413
4414   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4415   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4416   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4417
4418   cave->infinite_rockets                = level->bd_infinite_rockets;
4419
4420   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4421   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4422
4423   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4424   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4425
4426   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4427   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4428   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4429
4430   cave->gravity                         = level->bd_gravity_direction;
4431   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4432   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4433   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4434
4435   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4436   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4437   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4438   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4439
4440   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4441   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4442   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4443   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4444   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4445   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4446
4447   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4448   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4449   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4450   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4451
4452   cave->colorb                          = level->bd_color_b;
4453   cave->color0                          = level->bd_color_0;
4454   cave->color1                          = level->bd_color_1;
4455   cave->color2                          = level->bd_color_2;
4456   cave->color3                          = level->bd_color_3;
4457   cave->color4                          = level->bd_color_4;
4458   cave->color5                          = level->bd_color_5;
4459
4460   // level name
4461   strncpy(cave->name, level->name, sizeof(GdString));
4462   cave->name[sizeof(GdString) - 1] = '\0';
4463
4464   // playfield elements
4465   for (x = 0; x < cave->w; x++)
4466     for (y = 0; y < cave->h; y++)
4467       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4468 }
4469
4470 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4471 {
4472   struct LevelInfo_BD *level_bd = level->native_bd_level;
4473   GdCave *cave = level_bd->cave;
4474   int bd_level_nr = level_bd->level_nr;
4475   int x, y;
4476
4477   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4478   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4479
4480   // level type
4481   level->bd_intermission                = cave->intermission;
4482
4483   // level settings
4484   level->time                           = cave->level_time[bd_level_nr];
4485   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4486
4487   // game timing
4488   level->bd_scheduling_type             = cave->scheduling;
4489   level->bd_pal_timing                  = cave->pal_timing;
4490   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4491   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4492   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4493   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4494
4495   // scores
4496   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4497   level->score[SC_EMERALD]              = cave->diamond_value;
4498   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4499
4500   // compatibility settings
4501   level->bd_line_shifting_borders       = cave->lineshift;
4502   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4503   level->bd_short_explosions            = cave->short_explosions;
4504
4505   // player properties
4506   level->bd_diagonal_movements          = cave->diagonal_movements;
4507   level->bd_topmost_player_active       = cave->active_is_first_found;
4508   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4509   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4510   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4511   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4512
4513   // element properties
4514   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4515   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4516   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4517   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4518   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4519   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4520   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4521   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4522   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4523   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4524   level->bd_magic_wall_break_scan       = cave->magic_wall_breakscan;
4525
4526   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4527   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4528   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4529   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4530   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4531   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4532   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4533
4534   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4535   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4536   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4537   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4538   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4539   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4540   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4541   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4542   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4543   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4544   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4545
4546   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4547   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4548   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4549   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4550   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4551   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4552
4553   level->bd_slime_is_predictable        = cave->slime_predictable;
4554   level->bd_slime_correct_random        = cave->slime_correct_random;
4555   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4556   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4557   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4558   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4559   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4560   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4561   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4562   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4563   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4564   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4565
4566   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4567   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4568   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4569
4570   level->bd_biter_move_delay            = cave->biter_delay_frame;
4571   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4572
4573   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4574
4575   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4576
4577   level->bd_replicators_active          = cave->replicators_active;
4578   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4579
4580   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4581   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4582
4583   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4584
4585   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4586
4587   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4588   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4589   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4590
4591   level->bd_infinite_rockets            = cave->infinite_rockets;
4592
4593   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4594   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4595
4596   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4597   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4598
4599   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4600   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4601   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4602
4603   level->bd_gravity_direction           = cave->gravity;
4604   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4605   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4606   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4607
4608   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4609   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4610   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4611   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4612
4613   level->bd_firefly_explodes_to         = CAVE_TO_LEVEL(cave->firefly_explode_to);
4614   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4615   level->bd_butterfly_explodes_to       = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4616   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4617   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4618   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4619
4620   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4621   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4622   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4623   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4624
4625   level->bd_color_b                     = cave->colorb;
4626   level->bd_color_0                     = cave->color0;
4627   level->bd_color_1                     = cave->color1;
4628   level->bd_color_2                     = cave->color2;
4629   level->bd_color_3                     = cave->color3;
4630   level->bd_color_4                     = cave->color4;
4631   level->bd_color_5                     = cave->color5;
4632
4633   // set default color type and colors for BD style level colors
4634   SetDefaultLevelColorType_BD();
4635   SetDefaultLevelColors_BD();
4636
4637   // level name
4638   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4639
4640   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4641   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4642
4643   // playfield elements
4644   for (x = 0; x < level->fieldx; x++)
4645     for (y = 0; y < level->fieldy; y++)
4646       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4647
4648   checked_free(cave_name);
4649 }
4650
4651 static void setTapeInfoToDefaults(void);
4652
4653 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4654 {
4655   struct LevelInfo_BD *level_bd = level->native_bd_level;
4656   GdCave *cave = level_bd->cave;
4657   GdReplay *replay = level_bd->replay;
4658   int i;
4659
4660   if (replay == NULL)
4661     return;
4662
4663   // always start with reliable default values
4664   setTapeInfoToDefaults();
4665
4666   tape.level_nr = level_nr;             // (currently not used)
4667   tape.random_seed = replay->seed;
4668
4669   TapeSetDateFromIsoDateString(replay->date);
4670
4671   tape.counter = 0;
4672   tape.pos[tape.counter].delay = 0;
4673
4674   tape.bd_replay = TRUE;
4675
4676   // all time calculations only used to display approximate tape time
4677   int cave_speed = cave->speed;
4678   int milliseconds_game = 0;
4679   int milliseconds_elapsed = 20;
4680
4681   for (i = 0; i < replay->movements->len; i++)
4682   {
4683     int replay_action = replay->movements->data[i];
4684     int tape_action = map_action_BD_to_RND(replay_action);
4685     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4686     boolean success = 0;
4687
4688     while (1)
4689     {
4690       success = TapeAddAction(action);
4691
4692       milliseconds_game += milliseconds_elapsed;
4693
4694       if (milliseconds_game >= cave_speed)
4695       {
4696         milliseconds_game -= cave_speed;
4697
4698         break;
4699       }
4700     }
4701
4702     tape.counter++;
4703     tape.pos[tape.counter].delay = 0;
4704     tape.pos[tape.counter].action[0] = 0;
4705
4706     if (!success)
4707     {
4708       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4709
4710       break;
4711     }
4712   }
4713
4714   TapeHaltRecording();
4715
4716   if (!replay->success)
4717     Warn("BD replay is marked as not successful");
4718 }
4719
4720
4721 // ----------------------------------------------------------------------------
4722 // functions for loading EM level
4723 // ----------------------------------------------------------------------------
4724
4725 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4726 {
4727   static int ball_xy[8][2] =
4728   {
4729     { 0, 0 },
4730     { 1, 0 },
4731     { 2, 0 },
4732     { 0, 1 },
4733     { 2, 1 },
4734     { 0, 2 },
4735     { 1, 2 },
4736     { 2, 2 },
4737   };
4738   struct LevelInfo_EM *level_em = level->native_em_level;
4739   struct CAVE *cav = level_em->cav;
4740   int i, j, x, y;
4741
4742   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4743   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4744
4745   cav->time_seconds     = level->time;
4746   cav->gems_needed      = level->gems_needed;
4747
4748   cav->emerald_score    = level->score[SC_EMERALD];
4749   cav->diamond_score    = level->score[SC_DIAMOND];
4750   cav->alien_score      = level->score[SC_ROBOT];
4751   cav->tank_score       = level->score[SC_SPACESHIP];
4752   cav->bug_score        = level->score[SC_BUG];
4753   cav->eater_score      = level->score[SC_YAMYAM];
4754   cav->nut_score        = level->score[SC_NUT];
4755   cav->dynamite_score   = level->score[SC_DYNAMITE];
4756   cav->key_score        = level->score[SC_KEY];
4757   cav->exit_score       = level->score[SC_TIME_BONUS];
4758
4759   cav->num_eater_arrays = level->num_yamyam_contents;
4760
4761   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4762     for (y = 0; y < 3; y++)
4763       for (x = 0; x < 3; x++)
4764         cav->eater_array[i][y * 3 + x] =
4765           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4766
4767   cav->amoeba_time              = level->amoeba_speed;
4768   cav->wonderwall_time          = level->time_magic_wall;
4769   cav->wheel_time               = level->time_wheel;
4770
4771   cav->android_move_time        = level->android_move_time;
4772   cav->android_clone_time       = level->android_clone_time;
4773   cav->ball_random              = level->ball_random;
4774   cav->ball_active              = level->ball_active_initial;
4775   cav->ball_time                = level->ball_time;
4776   cav->num_ball_arrays          = level->num_ball_contents;
4777
4778   cav->lenses_score             = level->lenses_score;
4779   cav->magnify_score            = level->magnify_score;
4780   cav->slurp_score              = level->slurp_score;
4781
4782   cav->lenses_time              = level->lenses_time;
4783   cav->magnify_time             = level->magnify_time;
4784
4785   cav->wind_time = 9999;
4786   cav->wind_direction =
4787     map_direction_RND_to_EM(level->wind_direction_initial);
4788
4789   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4790     for (j = 0; j < 8; j++)
4791       cav->ball_array[i][j] =
4792         map_element_RND_to_EM_cave(level->ball_content[i].
4793                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4794
4795   map_android_clone_elements_RND_to_EM(level);
4796
4797   // first fill the complete playfield with the empty space element
4798   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4799     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4800       cav->cave[x][y] = Cblank;
4801
4802   // then copy the real level contents from level file into the playfield
4803   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4804   {
4805     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4806
4807     if (level->field[x][y] == EL_AMOEBA_DEAD)
4808       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4809
4810     cav->cave[x][y] = new_element;
4811   }
4812
4813   for (i = 0; i < MAX_PLAYERS; i++)
4814   {
4815     cav->player_x[i] = -1;
4816     cav->player_y[i] = -1;
4817   }
4818
4819   // initialize player positions and delete players from the playfield
4820   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4821   {
4822     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4823     {
4824       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4825
4826       cav->player_x[player_nr] = x;
4827       cav->player_y[player_nr] = y;
4828
4829       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4830     }
4831   }
4832 }
4833
4834 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4835 {
4836   static int ball_xy[8][2] =
4837   {
4838     { 0, 0 },
4839     { 1, 0 },
4840     { 2, 0 },
4841     { 0, 1 },
4842     { 2, 1 },
4843     { 0, 2 },
4844     { 1, 2 },
4845     { 2, 2 },
4846   };
4847   struct LevelInfo_EM *level_em = level->native_em_level;
4848   struct CAVE *cav = level_em->cav;
4849   int i, j, x, y;
4850
4851   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4852   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4853
4854   level->time        = cav->time_seconds;
4855   level->gems_needed = cav->gems_needed;
4856
4857   sprintf(level->name, "Level %d", level->file_info.nr);
4858
4859   level->score[SC_EMERALD]      = cav->emerald_score;
4860   level->score[SC_DIAMOND]      = cav->diamond_score;
4861   level->score[SC_ROBOT]        = cav->alien_score;
4862   level->score[SC_SPACESHIP]    = cav->tank_score;
4863   level->score[SC_BUG]          = cav->bug_score;
4864   level->score[SC_YAMYAM]       = cav->eater_score;
4865   level->score[SC_NUT]          = cav->nut_score;
4866   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4867   level->score[SC_KEY]          = cav->key_score;
4868   level->score[SC_TIME_BONUS]   = cav->exit_score;
4869
4870   level->num_yamyam_contents    = cav->num_eater_arrays;
4871
4872   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4873     for (y = 0; y < 3; y++)
4874       for (x = 0; x < 3; x++)
4875         level->yamyam_content[i].e[x][y] =
4876           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4877
4878   level->amoeba_speed           = cav->amoeba_time;
4879   level->time_magic_wall        = cav->wonderwall_time;
4880   level->time_wheel             = cav->wheel_time;
4881
4882   level->android_move_time      = cav->android_move_time;
4883   level->android_clone_time     = cav->android_clone_time;
4884   level->ball_random            = cav->ball_random;
4885   level->ball_active_initial    = cav->ball_active;
4886   level->ball_time              = cav->ball_time;
4887   level->num_ball_contents      = cav->num_ball_arrays;
4888
4889   level->lenses_score           = cav->lenses_score;
4890   level->magnify_score          = cav->magnify_score;
4891   level->slurp_score            = cav->slurp_score;
4892
4893   level->lenses_time            = cav->lenses_time;
4894   level->magnify_time           = cav->magnify_time;
4895
4896   level->wind_direction_initial =
4897     map_direction_EM_to_RND(cav->wind_direction);
4898
4899   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4900     for (j = 0; j < 8; j++)
4901       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4902         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4903
4904   map_android_clone_elements_EM_to_RND(level);
4905
4906   // convert the playfield (some elements need special treatment)
4907   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4908   {
4909     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4910
4911     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4912       new_element = EL_AMOEBA_DEAD;
4913
4914     level->field[x][y] = new_element;
4915   }
4916
4917   for (i = 0; i < MAX_PLAYERS; i++)
4918   {
4919     // in case of all players set to the same field, use the first player
4920     int nr = MAX_PLAYERS - i - 1;
4921     int jx = cav->player_x[nr];
4922     int jy = cav->player_y[nr];
4923
4924     if (jx != -1 && jy != -1)
4925       level->field[jx][jy] = EL_PLAYER_1 + nr;
4926   }
4927
4928   // time score is counted for each 10 seconds left in Emerald Mine levels
4929   level->time_score_base = 10;
4930 }
4931
4932
4933 // ----------------------------------------------------------------------------
4934 // functions for loading SP level
4935 // ----------------------------------------------------------------------------
4936
4937 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4938 {
4939   struct LevelInfo_SP *level_sp = level->native_sp_level;
4940   LevelInfoType *header = &level_sp->header;
4941   int i, x, y;
4942
4943   level_sp->width  = level->fieldx;
4944   level_sp->height = level->fieldy;
4945
4946   for (x = 0; x < level->fieldx; x++)
4947     for (y = 0; y < level->fieldy; y++)
4948       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4949
4950   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4951
4952   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4953     header->LevelTitle[i] = level->name[i];
4954   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4955
4956   header->InfotronsNeeded = level->gems_needed;
4957
4958   header->SpecialPortCount = 0;
4959
4960   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4961   {
4962     boolean gravity_port_found = FALSE;
4963     boolean gravity_port_valid = FALSE;
4964     int gravity_port_flag;
4965     int gravity_port_base_element;
4966     int element = level->field[x][y];
4967
4968     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4969         element <= EL_SP_GRAVITY_ON_PORT_UP)
4970     {
4971       gravity_port_found = TRUE;
4972       gravity_port_valid = TRUE;
4973       gravity_port_flag = 1;
4974       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4975     }
4976     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4977              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4978     {
4979       gravity_port_found = TRUE;
4980       gravity_port_valid = TRUE;
4981       gravity_port_flag = 0;
4982       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4983     }
4984     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4985              element <= EL_SP_GRAVITY_PORT_UP)
4986     {
4987       // change R'n'D style gravity inverting special port to normal port
4988       // (there are no gravity inverting ports in native Supaplex engine)
4989
4990       gravity_port_found = TRUE;
4991       gravity_port_valid = FALSE;
4992       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4993     }
4994
4995     if (gravity_port_found)
4996     {
4997       if (gravity_port_valid &&
4998           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4999       {
5000         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5001
5002         port->PortLocation = (y * level->fieldx + x) * 2;
5003         port->Gravity = gravity_port_flag;
5004
5005         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5006
5007         header->SpecialPortCount++;
5008       }
5009       else
5010       {
5011         // change special gravity port to normal port
5012
5013         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5014       }
5015
5016       level_sp->playfield[x][y] = element - EL_SP_START;
5017     }
5018   }
5019 }
5020
5021 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5022 {
5023   struct LevelInfo_SP *level_sp = level->native_sp_level;
5024   LevelInfoType *header = &level_sp->header;
5025   boolean num_invalid_elements = 0;
5026   int i, j, x, y;
5027
5028   level->fieldx = level_sp->width;
5029   level->fieldy = level_sp->height;
5030
5031   for (x = 0; x < level->fieldx; x++)
5032   {
5033     for (y = 0; y < level->fieldy; y++)
5034     {
5035       int element_old = level_sp->playfield[x][y];
5036       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5037
5038       if (element_new == EL_UNKNOWN)
5039       {
5040         num_invalid_elements++;
5041
5042         Debug("level:native:SP", "invalid element %d at position %d, %d",
5043               element_old, x, y);
5044       }
5045
5046       level->field[x][y] = element_new;
5047     }
5048   }
5049
5050   if (num_invalid_elements > 0)
5051     Warn("found %d invalid elements%s", num_invalid_elements,
5052          (!options.debug ? " (use '--debug' for more details)" : ""));
5053
5054   for (i = 0; i < MAX_PLAYERS; i++)
5055     level->initial_player_gravity[i] =
5056       (header->InitialGravity == 1 ? TRUE : FALSE);
5057
5058   // skip leading spaces
5059   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5060     if (header->LevelTitle[i] != ' ')
5061       break;
5062
5063   // copy level title
5064   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5065     level->name[j] = header->LevelTitle[i];
5066   level->name[j] = '\0';
5067
5068   // cut trailing spaces
5069   for (; j > 0; j--)
5070     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5071       level->name[j - 1] = '\0';
5072
5073   level->gems_needed = header->InfotronsNeeded;
5074
5075   for (i = 0; i < header->SpecialPortCount; i++)
5076   {
5077     SpecialPortType *port = &header->SpecialPort[i];
5078     int port_location = port->PortLocation;
5079     int gravity = port->Gravity;
5080     int port_x, port_y, port_element;
5081
5082     port_x = (port_location / 2) % level->fieldx;
5083     port_y = (port_location / 2) / level->fieldx;
5084
5085     if (port_x < 0 || port_x >= level->fieldx ||
5086         port_y < 0 || port_y >= level->fieldy)
5087     {
5088       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5089
5090       continue;
5091     }
5092
5093     port_element = level->field[port_x][port_y];
5094
5095     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5096         port_element > EL_SP_GRAVITY_PORT_UP)
5097     {
5098       Warn("no special port at position (%d, %d)", port_x, port_y);
5099
5100       continue;
5101     }
5102
5103     // change previous (wrong) gravity inverting special port to either
5104     // gravity enabling special port or gravity disabling special port
5105     level->field[port_x][port_y] +=
5106       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5107        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5108   }
5109
5110   // change special gravity ports without database entries to normal ports
5111   for (x = 0; x < level->fieldx; x++)
5112     for (y = 0; y < level->fieldy; y++)
5113       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5114           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5115         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5116
5117   level->time = 0;                      // no time limit
5118   level->amoeba_speed = 0;
5119   level->time_magic_wall = 0;
5120   level->time_wheel = 0;
5121   level->amoeba_content = EL_EMPTY;
5122
5123   // original Supaplex does not use score values -- rate by playing time
5124   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5125     level->score[i] = 0;
5126
5127   level->rate_time_over_score = TRUE;
5128
5129   // there are no yamyams in supaplex levels
5130   for (i = 0; i < level->num_yamyam_contents; i++)
5131     for (x = 0; x < 3; x++)
5132       for (y = 0; y < 3; y++)
5133         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5134 }
5135
5136 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5137 {
5138   struct LevelInfo_SP *level_sp = level->native_sp_level;
5139   struct DemoInfo_SP *demo = &level_sp->demo;
5140   int i, j;
5141
5142   // always start with reliable default values
5143   demo->is_available = FALSE;
5144   demo->length = 0;
5145
5146   if (TAPE_IS_EMPTY(tape))
5147     return;
5148
5149   demo->level_nr = tape.level_nr;       // (currently not used)
5150
5151   level_sp->header.DemoRandomSeed = tape.random_seed;
5152
5153   demo->length = 0;
5154
5155   for (i = 0; i < tape.length; i++)
5156   {
5157     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5158     int demo_repeat = tape.pos[i].delay;
5159     int demo_entries = (demo_repeat + 15) / 16;
5160
5161     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5162     {
5163       Warn("tape truncated: size exceeds maximum SP demo size %d",
5164            SP_MAX_TAPE_LEN);
5165
5166       break;
5167     }
5168
5169     for (j = 0; j < demo_repeat / 16; j++)
5170       demo->data[demo->length++] = 0xf0 | demo_action;
5171
5172     if (demo_repeat % 16)
5173       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5174   }
5175
5176   demo->is_available = TRUE;
5177 }
5178
5179 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5180 {
5181   struct LevelInfo_SP *level_sp = level->native_sp_level;
5182   struct DemoInfo_SP *demo = &level_sp->demo;
5183   char *filename = level->file_info.filename;
5184   int i;
5185
5186   // always start with reliable default values
5187   setTapeInfoToDefaults();
5188
5189   if (!demo->is_available)
5190     return;
5191
5192   tape.level_nr = demo->level_nr;       // (currently not used)
5193   tape.random_seed = level_sp->header.DemoRandomSeed;
5194
5195   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5196
5197   tape.counter = 0;
5198   tape.pos[tape.counter].delay = 0;
5199
5200   for (i = 0; i < demo->length; i++)
5201   {
5202     int demo_action = demo->data[i] & 0x0f;
5203     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5204     int tape_action = map_key_SP_to_RND(demo_action);
5205     int tape_repeat = demo_repeat + 1;
5206     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5207     boolean success = 0;
5208     int j;
5209
5210     for (j = 0; j < tape_repeat; j++)
5211       success = TapeAddAction(action);
5212
5213     if (!success)
5214     {
5215       Warn("SP demo truncated: size exceeds maximum tape size %d",
5216            MAX_TAPE_LEN);
5217
5218       break;
5219     }
5220   }
5221
5222   TapeHaltRecording();
5223 }
5224
5225
5226 // ----------------------------------------------------------------------------
5227 // functions for loading MM level
5228 // ----------------------------------------------------------------------------
5229
5230 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5231 {
5232   struct LevelInfo_MM *level_mm = level->native_mm_level;
5233   int i, x, y;
5234
5235   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5236   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5237
5238   level_mm->time = level->time;
5239   level_mm->kettles_needed = level->gems_needed;
5240   level_mm->auto_count_kettles = level->auto_count_gems;
5241
5242   level_mm->mm_laser_red   = level->mm_laser_red;
5243   level_mm->mm_laser_green = level->mm_laser_green;
5244   level_mm->mm_laser_blue  = level->mm_laser_blue;
5245
5246   level_mm->df_laser_red   = level->df_laser_red;
5247   level_mm->df_laser_green = level->df_laser_green;
5248   level_mm->df_laser_blue  = level->df_laser_blue;
5249
5250   strcpy(level_mm->name, level->name);
5251   strcpy(level_mm->author, level->author);
5252
5253   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5254   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5255   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5256   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5257   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5258
5259   level_mm->amoeba_speed = level->amoeba_speed;
5260   level_mm->time_fuse    = level->mm_time_fuse;
5261   level_mm->time_bomb    = level->mm_time_bomb;
5262   level_mm->time_ball    = level->mm_time_ball;
5263   level_mm->time_block   = level->mm_time_block;
5264
5265   level_mm->num_ball_contents = level->num_mm_ball_contents;
5266   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5267   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5268   level_mm->explode_ball = level->explode_mm_ball;
5269
5270   for (i = 0; i < level->num_mm_ball_contents; i++)
5271     level_mm->ball_content[i] =
5272       map_element_RND_to_MM(level->mm_ball_content[i]);
5273
5274   for (x = 0; x < level->fieldx; x++)
5275     for (y = 0; y < level->fieldy; y++)
5276       Ur[x][y] =
5277         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5278 }
5279
5280 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5281 {
5282   struct LevelInfo_MM *level_mm = level->native_mm_level;
5283   int i, x, y;
5284
5285   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5286   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5287
5288   level->time = level_mm->time;
5289   level->gems_needed = level_mm->kettles_needed;
5290   level->auto_count_gems = level_mm->auto_count_kettles;
5291
5292   level->mm_laser_red   = level_mm->mm_laser_red;
5293   level->mm_laser_green = level_mm->mm_laser_green;
5294   level->mm_laser_blue  = level_mm->mm_laser_blue;
5295
5296   level->df_laser_red   = level_mm->df_laser_red;
5297   level->df_laser_green = level_mm->df_laser_green;
5298   level->df_laser_blue  = level_mm->df_laser_blue;
5299
5300   strcpy(level->name, level_mm->name);
5301
5302   // only overwrite author from 'levelinfo.conf' if author defined in level
5303   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5304     strcpy(level->author, level_mm->author);
5305
5306   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5307   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5308   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5309   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5310   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5311
5312   level->amoeba_speed  = level_mm->amoeba_speed;
5313   level->mm_time_fuse  = level_mm->time_fuse;
5314   level->mm_time_bomb  = level_mm->time_bomb;
5315   level->mm_time_ball  = level_mm->time_ball;
5316   level->mm_time_block = level_mm->time_block;
5317
5318   level->num_mm_ball_contents = level_mm->num_ball_contents;
5319   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5320   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5321   level->explode_mm_ball = level_mm->explode_ball;
5322
5323   for (i = 0; i < level->num_mm_ball_contents; i++)
5324     level->mm_ball_content[i] =
5325       map_element_MM_to_RND(level_mm->ball_content[i]);
5326
5327   for (x = 0; x < level->fieldx; x++)
5328     for (y = 0; y < level->fieldy; y++)
5329       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5330 }
5331
5332
5333 // ----------------------------------------------------------------------------
5334 // functions for loading DC level
5335 // ----------------------------------------------------------------------------
5336
5337 #define DC_LEVEL_HEADER_SIZE            344
5338
5339 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5340                                         boolean init)
5341 {
5342   static int last_data_encoded;
5343   static int offset1;
5344   static int offset2;
5345   int diff;
5346   int diff_hi, diff_lo;
5347   int data_hi, data_lo;
5348   unsigned short data_decoded;
5349
5350   if (init)
5351   {
5352     last_data_encoded = 0;
5353     offset1 = -1;
5354     offset2 = 0;
5355
5356     return 0;
5357   }
5358
5359   diff = data_encoded - last_data_encoded;
5360   diff_hi = diff & ~0xff;
5361   diff_lo = diff &  0xff;
5362
5363   offset2 += diff_lo;
5364
5365   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5366   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5367   data_hi = data_hi & 0xff00;
5368
5369   data_decoded = data_hi | data_lo;
5370
5371   last_data_encoded = data_encoded;
5372
5373   offset1 = (offset1 + 1) % 31;
5374   offset2 = offset2 & 0xff;
5375
5376   return data_decoded;
5377 }
5378
5379 static int getMappedElement_DC(int element)
5380 {
5381   switch (element)
5382   {
5383     case 0x0000:
5384       element = EL_ROCK;
5385       break;
5386
5387       // 0x0117 - 0x036e: (?)
5388       // EL_DIAMOND
5389
5390       // 0x042d - 0x0684: (?)
5391       // EL_EMERALD
5392
5393     case 0x06f1:
5394       element = EL_NUT;
5395       break;
5396
5397     case 0x074c:
5398       element = EL_BOMB;
5399       break;
5400
5401     case 0x07a4:
5402       element = EL_PEARL;
5403       break;
5404
5405     case 0x0823:
5406       element = EL_CRYSTAL;
5407       break;
5408
5409     case 0x0e77:        // quicksand (boulder)
5410       element = EL_QUICKSAND_FAST_FULL;
5411       break;
5412
5413     case 0x0e99:        // slow quicksand (boulder)
5414       element = EL_QUICKSAND_FULL;
5415       break;
5416
5417     case 0x0ed2:
5418       element = EL_EM_EXIT_OPEN;
5419       break;
5420
5421     case 0x0ee3:
5422       element = EL_EM_EXIT_CLOSED;
5423       break;
5424
5425     case 0x0eeb:
5426       element = EL_EM_STEEL_EXIT_OPEN;
5427       break;
5428
5429     case 0x0efc:
5430       element = EL_EM_STEEL_EXIT_CLOSED;
5431       break;
5432
5433     case 0x0f4f:        // dynamite (lit 1)
5434       element = EL_EM_DYNAMITE_ACTIVE;
5435       break;
5436
5437     case 0x0f57:        // dynamite (lit 2)
5438       element = EL_EM_DYNAMITE_ACTIVE;
5439       break;
5440
5441     case 0x0f5f:        // dynamite (lit 3)
5442       element = EL_EM_DYNAMITE_ACTIVE;
5443       break;
5444
5445     case 0x0f67:        // dynamite (lit 4)
5446       element = EL_EM_DYNAMITE_ACTIVE;
5447       break;
5448
5449     case 0x0f81:
5450     case 0x0f82:
5451     case 0x0f83:
5452     case 0x0f84:
5453       element = EL_AMOEBA_WET;
5454       break;
5455
5456     case 0x0f85:
5457       element = EL_AMOEBA_DROP;
5458       break;
5459
5460     case 0x0fb9:
5461       element = EL_DC_MAGIC_WALL;
5462       break;
5463
5464     case 0x0fd0:
5465       element = EL_SPACESHIP_UP;
5466       break;
5467
5468     case 0x0fd9:
5469       element = EL_SPACESHIP_DOWN;
5470       break;
5471
5472     case 0x0ff1:
5473       element = EL_SPACESHIP_LEFT;
5474       break;
5475
5476     case 0x0ff9:
5477       element = EL_SPACESHIP_RIGHT;
5478       break;
5479
5480     case 0x1057:
5481       element = EL_BUG_UP;
5482       break;
5483
5484     case 0x1060:
5485       element = EL_BUG_DOWN;
5486       break;
5487
5488     case 0x1078:
5489       element = EL_BUG_LEFT;
5490       break;
5491
5492     case 0x1080:
5493       element = EL_BUG_RIGHT;
5494       break;
5495
5496     case 0x10de:
5497       element = EL_MOLE_UP;
5498       break;
5499
5500     case 0x10e7:
5501       element = EL_MOLE_DOWN;
5502       break;
5503
5504     case 0x10ff:
5505       element = EL_MOLE_LEFT;
5506       break;
5507
5508     case 0x1107:
5509       element = EL_MOLE_RIGHT;
5510       break;
5511
5512     case 0x11c0:
5513       element = EL_ROBOT;
5514       break;
5515
5516     case 0x13f5:
5517       element = EL_YAMYAM_UP;
5518       break;
5519
5520     case 0x1425:
5521       element = EL_SWITCHGATE_OPEN;
5522       break;
5523
5524     case 0x1426:
5525       element = EL_SWITCHGATE_CLOSED;
5526       break;
5527
5528     case 0x1437:
5529       element = EL_DC_SWITCHGATE_SWITCH_UP;
5530       break;
5531
5532     case 0x143a:
5533       element = EL_TIMEGATE_CLOSED;
5534       break;
5535
5536     case 0x144c:        // conveyor belt switch (green)
5537       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5538       break;
5539
5540     case 0x144f:        // conveyor belt switch (red)
5541       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5542       break;
5543
5544     case 0x1452:        // conveyor belt switch (blue)
5545       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5546       break;
5547
5548     case 0x145b:
5549       element = EL_CONVEYOR_BELT_3_MIDDLE;
5550       break;
5551
5552     case 0x1463:
5553       element = EL_CONVEYOR_BELT_3_LEFT;
5554       break;
5555
5556     case 0x146b:
5557       element = EL_CONVEYOR_BELT_3_RIGHT;
5558       break;
5559
5560     case 0x1473:
5561       element = EL_CONVEYOR_BELT_1_MIDDLE;
5562       break;
5563
5564     case 0x147b:
5565       element = EL_CONVEYOR_BELT_1_LEFT;
5566       break;
5567
5568     case 0x1483:
5569       element = EL_CONVEYOR_BELT_1_RIGHT;
5570       break;
5571
5572     case 0x148b:
5573       element = EL_CONVEYOR_BELT_4_MIDDLE;
5574       break;
5575
5576     case 0x1493:
5577       element = EL_CONVEYOR_BELT_4_LEFT;
5578       break;
5579
5580     case 0x149b:
5581       element = EL_CONVEYOR_BELT_4_RIGHT;
5582       break;
5583
5584     case 0x14ac:
5585       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5586       break;
5587
5588     case 0x14bd:
5589       element = EL_EXPANDABLE_WALL_VERTICAL;
5590       break;
5591
5592     case 0x14c6:
5593       element = EL_EXPANDABLE_WALL_ANY;
5594       break;
5595
5596     case 0x14ce:        // growing steel wall (left/right)
5597       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5598       break;
5599
5600     case 0x14df:        // growing steel wall (up/down)
5601       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5602       break;
5603
5604     case 0x14e8:        // growing steel wall (up/down/left/right)
5605       element = EL_EXPANDABLE_STEELWALL_ANY;
5606       break;
5607
5608     case 0x14e9:
5609       element = EL_SHIELD_DEADLY;
5610       break;
5611
5612     case 0x1501:
5613       element = EL_EXTRA_TIME;
5614       break;
5615
5616     case 0x154f:
5617       element = EL_ACID;
5618       break;
5619
5620     case 0x1577:
5621       element = EL_EMPTY_SPACE;
5622       break;
5623
5624     case 0x1578:        // quicksand (empty)
5625       element = EL_QUICKSAND_FAST_EMPTY;
5626       break;
5627
5628     case 0x1579:        // slow quicksand (empty)
5629       element = EL_QUICKSAND_EMPTY;
5630       break;
5631
5632       // 0x157c - 0x158b:
5633       // EL_SAND
5634
5635       // 0x1590 - 0x159f:
5636       // EL_DC_LANDMINE
5637
5638     case 0x15a0:
5639       element = EL_EM_DYNAMITE;
5640       break;
5641
5642     case 0x15a1:        // key (red)
5643       element = EL_EM_KEY_1;
5644       break;
5645
5646     case 0x15a2:        // key (yellow)
5647       element = EL_EM_KEY_2;
5648       break;
5649
5650     case 0x15a3:        // key (blue)
5651       element = EL_EM_KEY_4;
5652       break;
5653
5654     case 0x15a4:        // key (green)
5655       element = EL_EM_KEY_3;
5656       break;
5657
5658     case 0x15a5:        // key (white)
5659       element = EL_DC_KEY_WHITE;
5660       break;
5661
5662     case 0x15a6:
5663       element = EL_WALL_SLIPPERY;
5664       break;
5665
5666     case 0x15a7:
5667       element = EL_WALL;
5668       break;
5669
5670     case 0x15a8:        // wall (not round)
5671       element = EL_WALL;
5672       break;
5673
5674     case 0x15a9:        // (blue)
5675       element = EL_CHAR_A;
5676       break;
5677
5678     case 0x15aa:        // (blue)
5679       element = EL_CHAR_B;
5680       break;
5681
5682     case 0x15ab:        // (blue)
5683       element = EL_CHAR_C;
5684       break;
5685
5686     case 0x15ac:        // (blue)
5687       element = EL_CHAR_D;
5688       break;
5689
5690     case 0x15ad:        // (blue)
5691       element = EL_CHAR_E;
5692       break;
5693
5694     case 0x15ae:        // (blue)
5695       element = EL_CHAR_F;
5696       break;
5697
5698     case 0x15af:        // (blue)
5699       element = EL_CHAR_G;
5700       break;
5701
5702     case 0x15b0:        // (blue)
5703       element = EL_CHAR_H;
5704       break;
5705
5706     case 0x15b1:        // (blue)
5707       element = EL_CHAR_I;
5708       break;
5709
5710     case 0x15b2:        // (blue)
5711       element = EL_CHAR_J;
5712       break;
5713
5714     case 0x15b3:        // (blue)
5715       element = EL_CHAR_K;
5716       break;
5717
5718     case 0x15b4:        // (blue)
5719       element = EL_CHAR_L;
5720       break;
5721
5722     case 0x15b5:        // (blue)
5723       element = EL_CHAR_M;
5724       break;
5725
5726     case 0x15b6:        // (blue)
5727       element = EL_CHAR_N;
5728       break;
5729
5730     case 0x15b7:        // (blue)
5731       element = EL_CHAR_O;
5732       break;
5733
5734     case 0x15b8:        // (blue)
5735       element = EL_CHAR_P;
5736       break;
5737
5738     case 0x15b9:        // (blue)
5739       element = EL_CHAR_Q;
5740       break;
5741
5742     case 0x15ba:        // (blue)
5743       element = EL_CHAR_R;
5744       break;
5745
5746     case 0x15bb:        // (blue)
5747       element = EL_CHAR_S;
5748       break;
5749
5750     case 0x15bc:        // (blue)
5751       element = EL_CHAR_T;
5752       break;
5753
5754     case 0x15bd:        // (blue)
5755       element = EL_CHAR_U;
5756       break;
5757
5758     case 0x15be:        // (blue)
5759       element = EL_CHAR_V;
5760       break;
5761
5762     case 0x15bf:        // (blue)
5763       element = EL_CHAR_W;
5764       break;
5765
5766     case 0x15c0:        // (blue)
5767       element = EL_CHAR_X;
5768       break;
5769
5770     case 0x15c1:        // (blue)
5771       element = EL_CHAR_Y;
5772       break;
5773
5774     case 0x15c2:        // (blue)
5775       element = EL_CHAR_Z;
5776       break;
5777
5778     case 0x15c3:        // (blue)
5779       element = EL_CHAR_AUMLAUT;
5780       break;
5781
5782     case 0x15c4:        // (blue)
5783       element = EL_CHAR_OUMLAUT;
5784       break;
5785
5786     case 0x15c5:        // (blue)
5787       element = EL_CHAR_UUMLAUT;
5788       break;
5789
5790     case 0x15c6:        // (blue)
5791       element = EL_CHAR_0;
5792       break;
5793
5794     case 0x15c7:        // (blue)
5795       element = EL_CHAR_1;
5796       break;
5797
5798     case 0x15c8:        // (blue)
5799       element = EL_CHAR_2;
5800       break;
5801
5802     case 0x15c9:        // (blue)
5803       element = EL_CHAR_3;
5804       break;
5805
5806     case 0x15ca:        // (blue)
5807       element = EL_CHAR_4;
5808       break;
5809
5810     case 0x15cb:        // (blue)
5811       element = EL_CHAR_5;
5812       break;
5813
5814     case 0x15cc:        // (blue)
5815       element = EL_CHAR_6;
5816       break;
5817
5818     case 0x15cd:        // (blue)
5819       element = EL_CHAR_7;
5820       break;
5821
5822     case 0x15ce:        // (blue)
5823       element = EL_CHAR_8;
5824       break;
5825
5826     case 0x15cf:        // (blue)
5827       element = EL_CHAR_9;
5828       break;
5829
5830     case 0x15d0:        // (blue)
5831       element = EL_CHAR_PERIOD;
5832       break;
5833
5834     case 0x15d1:        // (blue)
5835       element = EL_CHAR_EXCLAM;
5836       break;
5837
5838     case 0x15d2:        // (blue)
5839       element = EL_CHAR_COLON;
5840       break;
5841
5842     case 0x15d3:        // (blue)
5843       element = EL_CHAR_LESS;
5844       break;
5845
5846     case 0x15d4:        // (blue)
5847       element = EL_CHAR_GREATER;
5848       break;
5849
5850     case 0x15d5:        // (blue)
5851       element = EL_CHAR_QUESTION;
5852       break;
5853
5854     case 0x15d6:        // (blue)
5855       element = EL_CHAR_COPYRIGHT;
5856       break;
5857
5858     case 0x15d7:        // (blue)
5859       element = EL_CHAR_UP;
5860       break;
5861
5862     case 0x15d8:        // (blue)
5863       element = EL_CHAR_DOWN;
5864       break;
5865
5866     case 0x15d9:        // (blue)
5867       element = EL_CHAR_BUTTON;
5868       break;
5869
5870     case 0x15da:        // (blue)
5871       element = EL_CHAR_PLUS;
5872       break;
5873
5874     case 0x15db:        // (blue)
5875       element = EL_CHAR_MINUS;
5876       break;
5877
5878     case 0x15dc:        // (blue)
5879       element = EL_CHAR_APOSTROPHE;
5880       break;
5881
5882     case 0x15dd:        // (blue)
5883       element = EL_CHAR_PARENLEFT;
5884       break;
5885
5886     case 0x15de:        // (blue)
5887       element = EL_CHAR_PARENRIGHT;
5888       break;
5889
5890     case 0x15df:        // (green)
5891       element = EL_CHAR_A;
5892       break;
5893
5894     case 0x15e0:        // (green)
5895       element = EL_CHAR_B;
5896       break;
5897
5898     case 0x15e1:        // (green)
5899       element = EL_CHAR_C;
5900       break;
5901
5902     case 0x15e2:        // (green)
5903       element = EL_CHAR_D;
5904       break;
5905
5906     case 0x15e3:        // (green)
5907       element = EL_CHAR_E;
5908       break;
5909
5910     case 0x15e4:        // (green)
5911       element = EL_CHAR_F;
5912       break;
5913
5914     case 0x15e5:        // (green)
5915       element = EL_CHAR_G;
5916       break;
5917
5918     case 0x15e6:        // (green)
5919       element = EL_CHAR_H;
5920       break;
5921
5922     case 0x15e7:        // (green)
5923       element = EL_CHAR_I;
5924       break;
5925
5926     case 0x15e8:        // (green)
5927       element = EL_CHAR_J;
5928       break;
5929
5930     case 0x15e9:        // (green)
5931       element = EL_CHAR_K;
5932       break;
5933
5934     case 0x15ea:        // (green)
5935       element = EL_CHAR_L;
5936       break;
5937
5938     case 0x15eb:        // (green)
5939       element = EL_CHAR_M;
5940       break;
5941
5942     case 0x15ec:        // (green)
5943       element = EL_CHAR_N;
5944       break;
5945
5946     case 0x15ed:        // (green)
5947       element = EL_CHAR_O;
5948       break;
5949
5950     case 0x15ee:        // (green)
5951       element = EL_CHAR_P;
5952       break;
5953
5954     case 0x15ef:        // (green)
5955       element = EL_CHAR_Q;
5956       break;
5957
5958     case 0x15f0:        // (green)
5959       element = EL_CHAR_R;
5960       break;
5961
5962     case 0x15f1:        // (green)
5963       element = EL_CHAR_S;
5964       break;
5965
5966     case 0x15f2:        // (green)
5967       element = EL_CHAR_T;
5968       break;
5969
5970     case 0x15f3:        // (green)
5971       element = EL_CHAR_U;
5972       break;
5973
5974     case 0x15f4:        // (green)
5975       element = EL_CHAR_V;
5976       break;
5977
5978     case 0x15f5:        // (green)
5979       element = EL_CHAR_W;
5980       break;
5981
5982     case 0x15f6:        // (green)
5983       element = EL_CHAR_X;
5984       break;
5985
5986     case 0x15f7:        // (green)
5987       element = EL_CHAR_Y;
5988       break;
5989
5990     case 0x15f8:        // (green)
5991       element = EL_CHAR_Z;
5992       break;
5993
5994     case 0x15f9:        // (green)
5995       element = EL_CHAR_AUMLAUT;
5996       break;
5997
5998     case 0x15fa:        // (green)
5999       element = EL_CHAR_OUMLAUT;
6000       break;
6001
6002     case 0x15fb:        // (green)
6003       element = EL_CHAR_UUMLAUT;
6004       break;
6005
6006     case 0x15fc:        // (green)
6007       element = EL_CHAR_0;
6008       break;
6009
6010     case 0x15fd:        // (green)
6011       element = EL_CHAR_1;
6012       break;
6013
6014     case 0x15fe:        // (green)
6015       element = EL_CHAR_2;
6016       break;
6017
6018     case 0x15ff:        // (green)
6019       element = EL_CHAR_3;
6020       break;
6021
6022     case 0x1600:        // (green)
6023       element = EL_CHAR_4;
6024       break;
6025
6026     case 0x1601:        // (green)
6027       element = EL_CHAR_5;
6028       break;
6029
6030     case 0x1602:        // (green)
6031       element = EL_CHAR_6;
6032       break;
6033
6034     case 0x1603:        // (green)
6035       element = EL_CHAR_7;
6036       break;
6037
6038     case 0x1604:        // (green)
6039       element = EL_CHAR_8;
6040       break;
6041
6042     case 0x1605:        // (green)
6043       element = EL_CHAR_9;
6044       break;
6045
6046     case 0x1606:        // (green)
6047       element = EL_CHAR_PERIOD;
6048       break;
6049
6050     case 0x1607:        // (green)
6051       element = EL_CHAR_EXCLAM;
6052       break;
6053
6054     case 0x1608:        // (green)
6055       element = EL_CHAR_COLON;
6056       break;
6057
6058     case 0x1609:        // (green)
6059       element = EL_CHAR_LESS;
6060       break;
6061
6062     case 0x160a:        // (green)
6063       element = EL_CHAR_GREATER;
6064       break;
6065
6066     case 0x160b:        // (green)
6067       element = EL_CHAR_QUESTION;
6068       break;
6069
6070     case 0x160c:        // (green)
6071       element = EL_CHAR_COPYRIGHT;
6072       break;
6073
6074     case 0x160d:        // (green)
6075       element = EL_CHAR_UP;
6076       break;
6077
6078     case 0x160e:        // (green)
6079       element = EL_CHAR_DOWN;
6080       break;
6081
6082     case 0x160f:        // (green)
6083       element = EL_CHAR_BUTTON;
6084       break;
6085
6086     case 0x1610:        // (green)
6087       element = EL_CHAR_PLUS;
6088       break;
6089
6090     case 0x1611:        // (green)
6091       element = EL_CHAR_MINUS;
6092       break;
6093
6094     case 0x1612:        // (green)
6095       element = EL_CHAR_APOSTROPHE;
6096       break;
6097
6098     case 0x1613:        // (green)
6099       element = EL_CHAR_PARENLEFT;
6100       break;
6101
6102     case 0x1614:        // (green)
6103       element = EL_CHAR_PARENRIGHT;
6104       break;
6105
6106     case 0x1615:        // (blue steel)
6107       element = EL_STEEL_CHAR_A;
6108       break;
6109
6110     case 0x1616:        // (blue steel)
6111       element = EL_STEEL_CHAR_B;
6112       break;
6113
6114     case 0x1617:        // (blue steel)
6115       element = EL_STEEL_CHAR_C;
6116       break;
6117
6118     case 0x1618:        // (blue steel)
6119       element = EL_STEEL_CHAR_D;
6120       break;
6121
6122     case 0x1619:        // (blue steel)
6123       element = EL_STEEL_CHAR_E;
6124       break;
6125
6126     case 0x161a:        // (blue steel)
6127       element = EL_STEEL_CHAR_F;
6128       break;
6129
6130     case 0x161b:        // (blue steel)
6131       element = EL_STEEL_CHAR_G;
6132       break;
6133
6134     case 0x161c:        // (blue steel)
6135       element = EL_STEEL_CHAR_H;
6136       break;
6137
6138     case 0x161d:        // (blue steel)
6139       element = EL_STEEL_CHAR_I;
6140       break;
6141
6142     case 0x161e:        // (blue steel)
6143       element = EL_STEEL_CHAR_J;
6144       break;
6145
6146     case 0x161f:        // (blue steel)
6147       element = EL_STEEL_CHAR_K;
6148       break;
6149
6150     case 0x1620:        // (blue steel)
6151       element = EL_STEEL_CHAR_L;
6152       break;
6153
6154     case 0x1621:        // (blue steel)
6155       element = EL_STEEL_CHAR_M;
6156       break;
6157
6158     case 0x1622:        // (blue steel)
6159       element = EL_STEEL_CHAR_N;
6160       break;
6161
6162     case 0x1623:        // (blue steel)
6163       element = EL_STEEL_CHAR_O;
6164       break;
6165
6166     case 0x1624:        // (blue steel)
6167       element = EL_STEEL_CHAR_P;
6168       break;
6169
6170     case 0x1625:        // (blue steel)
6171       element = EL_STEEL_CHAR_Q;
6172       break;
6173
6174     case 0x1626:        // (blue steel)
6175       element = EL_STEEL_CHAR_R;
6176       break;
6177
6178     case 0x1627:        // (blue steel)
6179       element = EL_STEEL_CHAR_S;
6180       break;
6181
6182     case 0x1628:        // (blue steel)
6183       element = EL_STEEL_CHAR_T;
6184       break;
6185
6186     case 0x1629:        // (blue steel)
6187       element = EL_STEEL_CHAR_U;
6188       break;
6189
6190     case 0x162a:        // (blue steel)
6191       element = EL_STEEL_CHAR_V;
6192       break;
6193
6194     case 0x162b:        // (blue steel)
6195       element = EL_STEEL_CHAR_W;
6196       break;
6197
6198     case 0x162c:        // (blue steel)
6199       element = EL_STEEL_CHAR_X;
6200       break;
6201
6202     case 0x162d:        // (blue steel)
6203       element = EL_STEEL_CHAR_Y;
6204       break;
6205
6206     case 0x162e:        // (blue steel)
6207       element = EL_STEEL_CHAR_Z;
6208       break;
6209
6210     case 0x162f:        // (blue steel)
6211       element = EL_STEEL_CHAR_AUMLAUT;
6212       break;
6213
6214     case 0x1630:        // (blue steel)
6215       element = EL_STEEL_CHAR_OUMLAUT;
6216       break;
6217
6218     case 0x1631:        // (blue steel)
6219       element = EL_STEEL_CHAR_UUMLAUT;
6220       break;
6221
6222     case 0x1632:        // (blue steel)
6223       element = EL_STEEL_CHAR_0;
6224       break;
6225
6226     case 0x1633:        // (blue steel)
6227       element = EL_STEEL_CHAR_1;
6228       break;
6229
6230     case 0x1634:        // (blue steel)
6231       element = EL_STEEL_CHAR_2;
6232       break;
6233
6234     case 0x1635:        // (blue steel)
6235       element = EL_STEEL_CHAR_3;
6236       break;
6237
6238     case 0x1636:        // (blue steel)
6239       element = EL_STEEL_CHAR_4;
6240       break;
6241
6242     case 0x1637:        // (blue steel)
6243       element = EL_STEEL_CHAR_5;
6244       break;
6245
6246     case 0x1638:        // (blue steel)
6247       element = EL_STEEL_CHAR_6;
6248       break;
6249
6250     case 0x1639:        // (blue steel)
6251       element = EL_STEEL_CHAR_7;
6252       break;
6253
6254     case 0x163a:        // (blue steel)
6255       element = EL_STEEL_CHAR_8;
6256       break;
6257
6258     case 0x163b:        // (blue steel)
6259       element = EL_STEEL_CHAR_9;
6260       break;
6261
6262     case 0x163c:        // (blue steel)
6263       element = EL_STEEL_CHAR_PERIOD;
6264       break;
6265
6266     case 0x163d:        // (blue steel)
6267       element = EL_STEEL_CHAR_EXCLAM;
6268       break;
6269
6270     case 0x163e:        // (blue steel)
6271       element = EL_STEEL_CHAR_COLON;
6272       break;
6273
6274     case 0x163f:        // (blue steel)
6275       element = EL_STEEL_CHAR_LESS;
6276       break;
6277
6278     case 0x1640:        // (blue steel)
6279       element = EL_STEEL_CHAR_GREATER;
6280       break;
6281
6282     case 0x1641:        // (blue steel)
6283       element = EL_STEEL_CHAR_QUESTION;
6284       break;
6285
6286     case 0x1642:        // (blue steel)
6287       element = EL_STEEL_CHAR_COPYRIGHT;
6288       break;
6289
6290     case 0x1643:        // (blue steel)
6291       element = EL_STEEL_CHAR_UP;
6292       break;
6293
6294     case 0x1644:        // (blue steel)
6295       element = EL_STEEL_CHAR_DOWN;
6296       break;
6297
6298     case 0x1645:        // (blue steel)
6299       element = EL_STEEL_CHAR_BUTTON;
6300       break;
6301
6302     case 0x1646:        // (blue steel)
6303       element = EL_STEEL_CHAR_PLUS;
6304       break;
6305
6306     case 0x1647:        // (blue steel)
6307       element = EL_STEEL_CHAR_MINUS;
6308       break;
6309
6310     case 0x1648:        // (blue steel)
6311       element = EL_STEEL_CHAR_APOSTROPHE;
6312       break;
6313
6314     case 0x1649:        // (blue steel)
6315       element = EL_STEEL_CHAR_PARENLEFT;
6316       break;
6317
6318     case 0x164a:        // (blue steel)
6319       element = EL_STEEL_CHAR_PARENRIGHT;
6320       break;
6321
6322     case 0x164b:        // (green steel)
6323       element = EL_STEEL_CHAR_A;
6324       break;
6325
6326     case 0x164c:        // (green steel)
6327       element = EL_STEEL_CHAR_B;
6328       break;
6329
6330     case 0x164d:        // (green steel)
6331       element = EL_STEEL_CHAR_C;
6332       break;
6333
6334     case 0x164e:        // (green steel)
6335       element = EL_STEEL_CHAR_D;
6336       break;
6337
6338     case 0x164f:        // (green steel)
6339       element = EL_STEEL_CHAR_E;
6340       break;
6341
6342     case 0x1650:        // (green steel)
6343       element = EL_STEEL_CHAR_F;
6344       break;
6345
6346     case 0x1651:        // (green steel)
6347       element = EL_STEEL_CHAR_G;
6348       break;
6349
6350     case 0x1652:        // (green steel)
6351       element = EL_STEEL_CHAR_H;
6352       break;
6353
6354     case 0x1653:        // (green steel)
6355       element = EL_STEEL_CHAR_I;
6356       break;
6357
6358     case 0x1654:        // (green steel)
6359       element = EL_STEEL_CHAR_J;
6360       break;
6361
6362     case 0x1655:        // (green steel)
6363       element = EL_STEEL_CHAR_K;
6364       break;
6365
6366     case 0x1656:        // (green steel)
6367       element = EL_STEEL_CHAR_L;
6368       break;
6369
6370     case 0x1657:        // (green steel)
6371       element = EL_STEEL_CHAR_M;
6372       break;
6373
6374     case 0x1658:        // (green steel)
6375       element = EL_STEEL_CHAR_N;
6376       break;
6377
6378     case 0x1659:        // (green steel)
6379       element = EL_STEEL_CHAR_O;
6380       break;
6381
6382     case 0x165a:        // (green steel)
6383       element = EL_STEEL_CHAR_P;
6384       break;
6385
6386     case 0x165b:        // (green steel)
6387       element = EL_STEEL_CHAR_Q;
6388       break;
6389
6390     case 0x165c:        // (green steel)
6391       element = EL_STEEL_CHAR_R;
6392       break;
6393
6394     case 0x165d:        // (green steel)
6395       element = EL_STEEL_CHAR_S;
6396       break;
6397
6398     case 0x165e:        // (green steel)
6399       element = EL_STEEL_CHAR_T;
6400       break;
6401
6402     case 0x165f:        // (green steel)
6403       element = EL_STEEL_CHAR_U;
6404       break;
6405
6406     case 0x1660:        // (green steel)
6407       element = EL_STEEL_CHAR_V;
6408       break;
6409
6410     case 0x1661:        // (green steel)
6411       element = EL_STEEL_CHAR_W;
6412       break;
6413
6414     case 0x1662:        // (green steel)
6415       element = EL_STEEL_CHAR_X;
6416       break;
6417
6418     case 0x1663:        // (green steel)
6419       element = EL_STEEL_CHAR_Y;
6420       break;
6421
6422     case 0x1664:        // (green steel)
6423       element = EL_STEEL_CHAR_Z;
6424       break;
6425
6426     case 0x1665:        // (green steel)
6427       element = EL_STEEL_CHAR_AUMLAUT;
6428       break;
6429
6430     case 0x1666:        // (green steel)
6431       element = EL_STEEL_CHAR_OUMLAUT;
6432       break;
6433
6434     case 0x1667:        // (green steel)
6435       element = EL_STEEL_CHAR_UUMLAUT;
6436       break;
6437
6438     case 0x1668:        // (green steel)
6439       element = EL_STEEL_CHAR_0;
6440       break;
6441
6442     case 0x1669:        // (green steel)
6443       element = EL_STEEL_CHAR_1;
6444       break;
6445
6446     case 0x166a:        // (green steel)
6447       element = EL_STEEL_CHAR_2;
6448       break;
6449
6450     case 0x166b:        // (green steel)
6451       element = EL_STEEL_CHAR_3;
6452       break;
6453
6454     case 0x166c:        // (green steel)
6455       element = EL_STEEL_CHAR_4;
6456       break;
6457
6458     case 0x166d:        // (green steel)
6459       element = EL_STEEL_CHAR_5;
6460       break;
6461
6462     case 0x166e:        // (green steel)
6463       element = EL_STEEL_CHAR_6;
6464       break;
6465
6466     case 0x166f:        // (green steel)
6467       element = EL_STEEL_CHAR_7;
6468       break;
6469
6470     case 0x1670:        // (green steel)
6471       element = EL_STEEL_CHAR_8;
6472       break;
6473
6474     case 0x1671:        // (green steel)
6475       element = EL_STEEL_CHAR_9;
6476       break;
6477
6478     case 0x1672:        // (green steel)
6479       element = EL_STEEL_CHAR_PERIOD;
6480       break;
6481
6482     case 0x1673:        // (green steel)
6483       element = EL_STEEL_CHAR_EXCLAM;
6484       break;
6485
6486     case 0x1674:        // (green steel)
6487       element = EL_STEEL_CHAR_COLON;
6488       break;
6489
6490     case 0x1675:        // (green steel)
6491       element = EL_STEEL_CHAR_LESS;
6492       break;
6493
6494     case 0x1676:        // (green steel)
6495       element = EL_STEEL_CHAR_GREATER;
6496       break;
6497
6498     case 0x1677:        // (green steel)
6499       element = EL_STEEL_CHAR_QUESTION;
6500       break;
6501
6502     case 0x1678:        // (green steel)
6503       element = EL_STEEL_CHAR_COPYRIGHT;
6504       break;
6505
6506     case 0x1679:        // (green steel)
6507       element = EL_STEEL_CHAR_UP;
6508       break;
6509
6510     case 0x167a:        // (green steel)
6511       element = EL_STEEL_CHAR_DOWN;
6512       break;
6513
6514     case 0x167b:        // (green steel)
6515       element = EL_STEEL_CHAR_BUTTON;
6516       break;
6517
6518     case 0x167c:        // (green steel)
6519       element = EL_STEEL_CHAR_PLUS;
6520       break;
6521
6522     case 0x167d:        // (green steel)
6523       element = EL_STEEL_CHAR_MINUS;
6524       break;
6525
6526     case 0x167e:        // (green steel)
6527       element = EL_STEEL_CHAR_APOSTROPHE;
6528       break;
6529
6530     case 0x167f:        // (green steel)
6531       element = EL_STEEL_CHAR_PARENLEFT;
6532       break;
6533
6534     case 0x1680:        // (green steel)
6535       element = EL_STEEL_CHAR_PARENRIGHT;
6536       break;
6537
6538     case 0x1681:        // gate (red)
6539       element = EL_EM_GATE_1;
6540       break;
6541
6542     case 0x1682:        // secret gate (red)
6543       element = EL_EM_GATE_1_GRAY;
6544       break;
6545
6546     case 0x1683:        // gate (yellow)
6547       element = EL_EM_GATE_2;
6548       break;
6549
6550     case 0x1684:        // secret gate (yellow)
6551       element = EL_EM_GATE_2_GRAY;
6552       break;
6553
6554     case 0x1685:        // gate (blue)
6555       element = EL_EM_GATE_4;
6556       break;
6557
6558     case 0x1686:        // secret gate (blue)
6559       element = EL_EM_GATE_4_GRAY;
6560       break;
6561
6562     case 0x1687:        // gate (green)
6563       element = EL_EM_GATE_3;
6564       break;
6565
6566     case 0x1688:        // secret gate (green)
6567       element = EL_EM_GATE_3_GRAY;
6568       break;
6569
6570     case 0x1689:        // gate (white)
6571       element = EL_DC_GATE_WHITE;
6572       break;
6573
6574     case 0x168a:        // secret gate (white)
6575       element = EL_DC_GATE_WHITE_GRAY;
6576       break;
6577
6578     case 0x168b:        // secret gate (no key)
6579       element = EL_DC_GATE_FAKE_GRAY;
6580       break;
6581
6582     case 0x168c:
6583       element = EL_ROBOT_WHEEL;
6584       break;
6585
6586     case 0x168d:
6587       element = EL_DC_TIMEGATE_SWITCH;
6588       break;
6589
6590     case 0x168e:
6591       element = EL_ACID_POOL_BOTTOM;
6592       break;
6593
6594     case 0x168f:
6595       element = EL_ACID_POOL_TOPLEFT;
6596       break;
6597
6598     case 0x1690:
6599       element = EL_ACID_POOL_TOPRIGHT;
6600       break;
6601
6602     case 0x1691:
6603       element = EL_ACID_POOL_BOTTOMLEFT;
6604       break;
6605
6606     case 0x1692:
6607       element = EL_ACID_POOL_BOTTOMRIGHT;
6608       break;
6609
6610     case 0x1693:
6611       element = EL_STEELWALL;
6612       break;
6613
6614     case 0x1694:
6615       element = EL_STEELWALL_SLIPPERY;
6616       break;
6617
6618     case 0x1695:        // steel wall (not round)
6619       element = EL_STEELWALL;
6620       break;
6621
6622     case 0x1696:        // steel wall (left)
6623       element = EL_DC_STEELWALL_1_LEFT;
6624       break;
6625
6626     case 0x1697:        // steel wall (bottom)
6627       element = EL_DC_STEELWALL_1_BOTTOM;
6628       break;
6629
6630     case 0x1698:        // steel wall (right)
6631       element = EL_DC_STEELWALL_1_RIGHT;
6632       break;
6633
6634     case 0x1699:        // steel wall (top)
6635       element = EL_DC_STEELWALL_1_TOP;
6636       break;
6637
6638     case 0x169a:        // steel wall (left/bottom)
6639       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6640       break;
6641
6642     case 0x169b:        // steel wall (right/bottom)
6643       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6644       break;
6645
6646     case 0x169c:        // steel wall (right/top)
6647       element = EL_DC_STEELWALL_1_TOPRIGHT;
6648       break;
6649
6650     case 0x169d:        // steel wall (left/top)
6651       element = EL_DC_STEELWALL_1_TOPLEFT;
6652       break;
6653
6654     case 0x169e:        // steel wall (right/bottom small)
6655       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6656       break;
6657
6658     case 0x169f:        // steel wall (left/bottom small)
6659       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6660       break;
6661
6662     case 0x16a0:        // steel wall (right/top small)
6663       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6664       break;
6665
6666     case 0x16a1:        // steel wall (left/top small)
6667       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6668       break;
6669
6670     case 0x16a2:        // steel wall (left/right)
6671       element = EL_DC_STEELWALL_1_VERTICAL;
6672       break;
6673
6674     case 0x16a3:        // steel wall (top/bottom)
6675       element = EL_DC_STEELWALL_1_HORIZONTAL;
6676       break;
6677
6678     case 0x16a4:        // steel wall 2 (left end)
6679       element = EL_DC_STEELWALL_2_LEFT;
6680       break;
6681
6682     case 0x16a5:        // steel wall 2 (right end)
6683       element = EL_DC_STEELWALL_2_RIGHT;
6684       break;
6685
6686     case 0x16a6:        // steel wall 2 (top end)
6687       element = EL_DC_STEELWALL_2_TOP;
6688       break;
6689
6690     case 0x16a7:        // steel wall 2 (bottom end)
6691       element = EL_DC_STEELWALL_2_BOTTOM;
6692       break;
6693
6694     case 0x16a8:        // steel wall 2 (left/right)
6695       element = EL_DC_STEELWALL_2_HORIZONTAL;
6696       break;
6697
6698     case 0x16a9:        // steel wall 2 (up/down)
6699       element = EL_DC_STEELWALL_2_VERTICAL;
6700       break;
6701
6702     case 0x16aa:        // steel wall 2 (mid)
6703       element = EL_DC_STEELWALL_2_MIDDLE;
6704       break;
6705
6706     case 0x16ab:
6707       element = EL_SIGN_EXCLAMATION;
6708       break;
6709
6710     case 0x16ac:
6711       element = EL_SIGN_RADIOACTIVITY;
6712       break;
6713
6714     case 0x16ad:
6715       element = EL_SIGN_STOP;
6716       break;
6717
6718     case 0x16ae:
6719       element = EL_SIGN_WHEELCHAIR;
6720       break;
6721
6722     case 0x16af:
6723       element = EL_SIGN_PARKING;
6724       break;
6725
6726     case 0x16b0:
6727       element = EL_SIGN_NO_ENTRY;
6728       break;
6729
6730     case 0x16b1:
6731       element = EL_SIGN_HEART;
6732       break;
6733
6734     case 0x16b2:
6735       element = EL_SIGN_GIVE_WAY;
6736       break;
6737
6738     case 0x16b3:
6739       element = EL_SIGN_ENTRY_FORBIDDEN;
6740       break;
6741
6742     case 0x16b4:
6743       element = EL_SIGN_EMERGENCY_EXIT;
6744       break;
6745
6746     case 0x16b5:
6747       element = EL_SIGN_YIN_YANG;
6748       break;
6749
6750     case 0x16b6:
6751       element = EL_WALL_EMERALD;
6752       break;
6753
6754     case 0x16b7:
6755       element = EL_WALL_DIAMOND;
6756       break;
6757
6758     case 0x16b8:
6759       element = EL_WALL_PEARL;
6760       break;
6761
6762     case 0x16b9:
6763       element = EL_WALL_CRYSTAL;
6764       break;
6765
6766     case 0x16ba:
6767       element = EL_INVISIBLE_WALL;
6768       break;
6769
6770     case 0x16bb:
6771       element = EL_INVISIBLE_STEELWALL;
6772       break;
6773
6774       // 0x16bc - 0x16cb:
6775       // EL_INVISIBLE_SAND
6776
6777     case 0x16cc:
6778       element = EL_LIGHT_SWITCH;
6779       break;
6780
6781     case 0x16cd:
6782       element = EL_ENVELOPE_1;
6783       break;
6784
6785     default:
6786       if (element >= 0x0117 && element <= 0x036e)       // (?)
6787         element = EL_DIAMOND;
6788       else if (element >= 0x042d && element <= 0x0684)  // (?)
6789         element = EL_EMERALD;
6790       else if (element >= 0x157c && element <= 0x158b)
6791         element = EL_SAND;
6792       else if (element >= 0x1590 && element <= 0x159f)
6793         element = EL_DC_LANDMINE;
6794       else if (element >= 0x16bc && element <= 0x16cb)
6795         element = EL_INVISIBLE_SAND;
6796       else
6797       {
6798         Warn("unknown Diamond Caves element 0x%04x", element);
6799
6800         element = EL_UNKNOWN;
6801       }
6802       break;
6803   }
6804
6805   return getMappedElement(element);
6806 }
6807
6808 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6809 {
6810   byte header[DC_LEVEL_HEADER_SIZE];
6811   int envelope_size;
6812   int envelope_header_pos = 62;
6813   int envelope_content_pos = 94;
6814   int level_name_pos = 251;
6815   int level_author_pos = 292;
6816   int envelope_header_len;
6817   int envelope_content_len;
6818   int level_name_len;
6819   int level_author_len;
6820   int fieldx, fieldy;
6821   int num_yamyam_contents;
6822   int i, x, y;
6823
6824   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6825
6826   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6827   {
6828     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6829
6830     header[i * 2 + 0] = header_word >> 8;
6831     header[i * 2 + 1] = header_word & 0xff;
6832   }
6833
6834   // read some values from level header to check level decoding integrity
6835   fieldx = header[6] | (header[7] << 8);
6836   fieldy = header[8] | (header[9] << 8);
6837   num_yamyam_contents = header[60] | (header[61] << 8);
6838
6839   // do some simple sanity checks to ensure that level was correctly decoded
6840   if (fieldx < 1 || fieldx > 256 ||
6841       fieldy < 1 || fieldy > 256 ||
6842       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6843   {
6844     level->no_valid_file = TRUE;
6845
6846     Warn("cannot decode level from stream -- using empty level");
6847
6848     return;
6849   }
6850
6851   // maximum envelope header size is 31 bytes
6852   envelope_header_len   = header[envelope_header_pos];
6853   // maximum envelope content size is 110 (156?) bytes
6854   envelope_content_len  = header[envelope_content_pos];
6855
6856   // maximum level title size is 40 bytes
6857   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6858   // maximum level author size is 30 (51?) bytes
6859   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6860
6861   envelope_size = 0;
6862
6863   for (i = 0; i < envelope_header_len; i++)
6864     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6865       level->envelope[0].text[envelope_size++] =
6866         header[envelope_header_pos + 1 + i];
6867
6868   if (envelope_header_len > 0 && envelope_content_len > 0)
6869   {
6870     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6871       level->envelope[0].text[envelope_size++] = '\n';
6872     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6873       level->envelope[0].text[envelope_size++] = '\n';
6874   }
6875
6876   for (i = 0; i < envelope_content_len; i++)
6877     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6878       level->envelope[0].text[envelope_size++] =
6879         header[envelope_content_pos + 1 + i];
6880
6881   level->envelope[0].text[envelope_size] = '\0';
6882
6883   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6884   level->envelope[0].ysize = 10;
6885   level->envelope[0].autowrap = TRUE;
6886   level->envelope[0].centered = TRUE;
6887
6888   for (i = 0; i < level_name_len; i++)
6889     level->name[i] = header[level_name_pos + 1 + i];
6890   level->name[level_name_len] = '\0';
6891
6892   for (i = 0; i < level_author_len; i++)
6893     level->author[i] = header[level_author_pos + 1 + i];
6894   level->author[level_author_len] = '\0';
6895
6896   num_yamyam_contents = header[60] | (header[61] << 8);
6897   level->num_yamyam_contents =
6898     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6899
6900   for (i = 0; i < num_yamyam_contents; i++)
6901   {
6902     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6903     {
6904       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6905       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6906
6907       if (i < MAX_ELEMENT_CONTENTS)
6908         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6909     }
6910   }
6911
6912   fieldx = header[6] | (header[7] << 8);
6913   fieldy = header[8] | (header[9] << 8);
6914   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6915   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6916
6917   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6918   {
6919     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6920     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6921
6922     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6923       level->field[x][y] = getMappedElement_DC(element_dc);
6924   }
6925
6926   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6927   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6928   level->field[x][y] = EL_PLAYER_1;
6929
6930   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6931   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6932   level->field[x][y] = EL_PLAYER_2;
6933
6934   level->gems_needed            = header[18] | (header[19] << 8);
6935
6936   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6937   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6938   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6939   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6940   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6941   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6942   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6943   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6944   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6945   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6946   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6947   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6948
6949   level->time                   = header[44] | (header[45] << 8);
6950
6951   level->amoeba_speed           = header[46] | (header[47] << 8);
6952   level->time_light             = header[48] | (header[49] << 8);
6953   level->time_timegate          = header[50] | (header[51] << 8);
6954   level->time_wheel             = header[52] | (header[53] << 8);
6955   level->time_magic_wall        = header[54] | (header[55] << 8);
6956   level->extra_time             = header[56] | (header[57] << 8);
6957   level->shield_normal_time     = header[58] | (header[59] << 8);
6958
6959   // shield and extra time elements do not have a score
6960   level->score[SC_SHIELD]       = 0;
6961   level->extra_time_score       = 0;
6962
6963   // set time for normal and deadly shields to the same value
6964   level->shield_deadly_time     = level->shield_normal_time;
6965
6966   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6967   // can slip down from flat walls, like normal walls and steel walls
6968   level->em_slippery_gems = TRUE;
6969
6970   // time score is counted for each 10 seconds left in Diamond Caves levels
6971   level->time_score_base = 10;
6972 }
6973
6974 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6975                                      struct LevelFileInfo *level_file_info,
6976                                      boolean level_info_only)
6977 {
6978   char *filename = level_file_info->filename;
6979   File *file;
6980   int num_magic_bytes = 8;
6981   char magic_bytes[num_magic_bytes + 1];
6982   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6983
6984   if (!(file = openFile(filename, MODE_READ)))
6985   {
6986     level->no_valid_file = TRUE;
6987
6988     if (!level_info_only)
6989       Warn("cannot read level '%s' -- using empty level", filename);
6990
6991     return;
6992   }
6993
6994   // fseek(file, 0x0000, SEEK_SET);
6995
6996   if (level_file_info->packed)
6997   {
6998     // read "magic bytes" from start of file
6999     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7000       magic_bytes[0] = '\0';
7001
7002     // check "magic bytes" for correct file format
7003     if (!strPrefix(magic_bytes, "DC2"))
7004     {
7005       level->no_valid_file = TRUE;
7006
7007       Warn("unknown DC level file '%s' -- using empty level", filename);
7008
7009       return;
7010     }
7011
7012     if (strPrefix(magic_bytes, "DC2Win95") ||
7013         strPrefix(magic_bytes, "DC2Win98"))
7014     {
7015       int position_first_level = 0x00fa;
7016       int extra_bytes = 4;
7017       int skip_bytes;
7018
7019       // advance file stream to first level inside the level package
7020       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7021
7022       // each block of level data is followed by block of non-level data
7023       num_levels_to_skip *= 2;
7024
7025       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7026       while (num_levels_to_skip >= 0)
7027       {
7028         // advance file stream to next level inside the level package
7029         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7030         {
7031           level->no_valid_file = TRUE;
7032
7033           Warn("cannot fseek in file '%s' -- using empty level", filename);
7034
7035           return;
7036         }
7037
7038         // skip apparently unused extra bytes following each level
7039         ReadUnusedBytesFromFile(file, extra_bytes);
7040
7041         // read size of next level in level package
7042         skip_bytes = getFile32BitLE(file);
7043
7044         num_levels_to_skip--;
7045       }
7046     }
7047     else
7048     {
7049       level->no_valid_file = TRUE;
7050
7051       Warn("unknown DC2 level file '%s' -- using empty level", filename);
7052
7053       return;
7054     }
7055   }
7056
7057   LoadLevelFromFileStream_DC(file, level);
7058
7059   closeFile(file);
7060 }
7061
7062
7063 // ----------------------------------------------------------------------------
7064 // functions for loading SB level
7065 // ----------------------------------------------------------------------------
7066
7067 int getMappedElement_SB(int element_ascii, boolean use_ces)
7068 {
7069   static struct
7070   {
7071     int ascii;
7072     int sb;
7073     int ce;
7074   }
7075   sb_element_mapping[] =
7076   {
7077     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
7078     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
7079     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
7080     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
7081     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
7082     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
7083     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
7084     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
7085
7086     { 0,   -1,                      -1          },
7087   };
7088
7089   int i;
7090
7091   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7092     if (element_ascii == sb_element_mapping[i].ascii)
7093       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7094
7095   return EL_UNDEFINED;
7096 }
7097
7098 static void SetLevelSettings_SB(struct LevelInfo *level)
7099 {
7100   // time settings
7101   level->time = 0;
7102   level->use_step_counter = TRUE;
7103
7104   // score settings
7105   level->score[SC_TIME_BONUS] = 0;
7106   level->time_score_base = 1;
7107   level->rate_time_over_score = TRUE;
7108
7109   // game settings
7110   level->auto_exit_sokoban = TRUE;
7111 }
7112
7113 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7114                                      struct LevelFileInfo *level_file_info,
7115                                      boolean level_info_only)
7116 {
7117   char *filename = level_file_info->filename;
7118   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7119   char last_comment[MAX_LINE_LEN];
7120   char level_name[MAX_LINE_LEN];
7121   char *line_ptr;
7122   File *file;
7123   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7124   boolean read_continued_line = FALSE;
7125   boolean reading_playfield = FALSE;
7126   boolean got_valid_playfield_line = FALSE;
7127   boolean invalid_playfield_char = FALSE;
7128   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7129   int file_level_nr = 0;
7130   int x = 0, y = 0;             // initialized to make compilers happy
7131
7132   last_comment[0] = '\0';
7133   level_name[0] = '\0';
7134
7135   if (!(file = openFile(filename, MODE_READ)))
7136   {
7137     level->no_valid_file = TRUE;
7138
7139     if (!level_info_only)
7140       Warn("cannot read level '%s' -- using empty level", filename);
7141
7142     return;
7143   }
7144
7145   while (!checkEndOfFile(file))
7146   {
7147     // level successfully read, but next level may follow here
7148     if (!got_valid_playfield_line && reading_playfield)
7149     {
7150       // read playfield from single level file -- skip remaining file
7151       if (!level_file_info->packed)
7152         break;
7153
7154       if (file_level_nr >= num_levels_to_skip)
7155         break;
7156
7157       file_level_nr++;
7158
7159       last_comment[0] = '\0';
7160       level_name[0] = '\0';
7161
7162       reading_playfield = FALSE;
7163     }
7164
7165     got_valid_playfield_line = FALSE;
7166
7167     // read next line of input file
7168     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7169       break;
7170
7171     // cut trailing line break (this can be newline and/or carriage return)
7172     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7173       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7174         *line_ptr = '\0';
7175
7176     // copy raw input line for later use (mainly debugging output)
7177     strcpy(line_raw, line);
7178
7179     if (read_continued_line)
7180     {
7181       // append new line to existing line, if there is enough space
7182       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7183         strcat(previous_line, line_ptr);
7184
7185       strcpy(line, previous_line);      // copy storage buffer to line
7186
7187       read_continued_line = FALSE;
7188     }
7189
7190     // if the last character is '\', continue at next line
7191     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7192     {
7193       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7194       strcpy(previous_line, line);      // copy line to storage buffer
7195
7196       read_continued_line = TRUE;
7197
7198       continue;
7199     }
7200
7201     // skip empty lines
7202     if (line[0] == '\0')
7203       continue;
7204
7205     // extract comment text from comment line
7206     if (line[0] == ';')
7207     {
7208       for (line_ptr = line; *line_ptr; line_ptr++)
7209         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7210           break;
7211
7212       strcpy(last_comment, line_ptr);
7213
7214       continue;
7215     }
7216
7217     // extract level title text from line containing level title
7218     if (line[0] == '\'')
7219     {
7220       strcpy(level_name, &line[1]);
7221
7222       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7223         level_name[strlen(level_name) - 1] = '\0';
7224
7225       continue;
7226     }
7227
7228     // skip lines containing only spaces (or empty lines)
7229     for (line_ptr = line; *line_ptr; line_ptr++)
7230       if (*line_ptr != ' ')
7231         break;
7232     if (*line_ptr == '\0')
7233       continue;
7234
7235     // at this point, we have found a line containing part of a playfield
7236
7237     got_valid_playfield_line = TRUE;
7238
7239     if (!reading_playfield)
7240     {
7241       reading_playfield = TRUE;
7242       invalid_playfield_char = FALSE;
7243
7244       for (x = 0; x < MAX_LEV_FIELDX; x++)
7245         for (y = 0; y < MAX_LEV_FIELDY; y++)
7246           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7247
7248       level->fieldx = 0;
7249       level->fieldy = 0;
7250
7251       // start with topmost tile row
7252       y = 0;
7253     }
7254
7255     // skip playfield line if larger row than allowed
7256     if (y >= MAX_LEV_FIELDY)
7257       continue;
7258
7259     // start with leftmost tile column
7260     x = 0;
7261
7262     // read playfield elements from line
7263     for (line_ptr = line; *line_ptr; line_ptr++)
7264     {
7265       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7266
7267       // stop parsing playfield line if larger column than allowed
7268       if (x >= MAX_LEV_FIELDX)
7269         break;
7270
7271       if (mapped_sb_element == EL_UNDEFINED)
7272       {
7273         invalid_playfield_char = TRUE;
7274
7275         break;
7276       }
7277
7278       level->field[x][y] = mapped_sb_element;
7279
7280       // continue with next tile column
7281       x++;
7282
7283       level->fieldx = MAX(x, level->fieldx);
7284     }
7285
7286     if (invalid_playfield_char)
7287     {
7288       // if first playfield line, treat invalid lines as comment lines
7289       if (y == 0)
7290         reading_playfield = FALSE;
7291
7292       continue;
7293     }
7294
7295     // continue with next tile row
7296     y++;
7297   }
7298
7299   closeFile(file);
7300
7301   level->fieldy = y;
7302
7303   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7304   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7305
7306   if (!reading_playfield)
7307   {
7308     level->no_valid_file = TRUE;
7309
7310     Warn("cannot read level '%s' -- using empty level", filename);
7311
7312     return;
7313   }
7314
7315   if (*level_name != '\0')
7316   {
7317     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7318     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7319   }
7320   else if (*last_comment != '\0')
7321   {
7322     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7323     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7324   }
7325   else
7326   {
7327     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7328   }
7329
7330   // set all empty fields beyond the border walls to invisible steel wall
7331   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7332   {
7333     if ((x == 0 || x == level->fieldx - 1 ||
7334          y == 0 || y == level->fieldy - 1) &&
7335         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7336       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7337                      level->field, level->fieldx, level->fieldy);
7338   }
7339
7340   // set special level settings for Sokoban levels
7341   SetLevelSettings_SB(level);
7342
7343   if (load_xsb_to_ces)
7344   {
7345     // special global settings can now be set in level template
7346     level->use_custom_template = TRUE;
7347   }
7348 }
7349
7350
7351 // -------------------------------------------------------------------------
7352 // functions for handling native levels
7353 // -------------------------------------------------------------------------
7354
7355 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7356                                      struct LevelFileInfo *level_file_info,
7357                                      boolean level_info_only)
7358 {
7359   int pos = 0;
7360
7361   // determine position of requested level inside level package
7362   if (level_file_info->packed)
7363     pos = level_file_info->nr - leveldir_current->first_level;
7364
7365   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7366     level->no_valid_file = TRUE;
7367 }
7368
7369 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7370                                      struct LevelFileInfo *level_file_info,
7371                                      boolean level_info_only)
7372 {
7373   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7374     level->no_valid_file = TRUE;
7375 }
7376
7377 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7378                                      struct LevelFileInfo *level_file_info,
7379                                      boolean level_info_only)
7380 {
7381   int pos = 0;
7382
7383   // determine position of requested level inside level package
7384   if (level_file_info->packed)
7385     pos = level_file_info->nr - leveldir_current->first_level;
7386
7387   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7388     level->no_valid_file = TRUE;
7389 }
7390
7391 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7392                                      struct LevelFileInfo *level_file_info,
7393                                      boolean level_info_only)
7394 {
7395   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7396     level->no_valid_file = TRUE;
7397 }
7398
7399 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7400 {
7401   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7402     CopyNativeLevel_RND_to_BD(level);
7403   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7404     CopyNativeLevel_RND_to_EM(level);
7405   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7406     CopyNativeLevel_RND_to_SP(level);
7407   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7408     CopyNativeLevel_RND_to_MM(level);
7409 }
7410
7411 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7412 {
7413   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7414     CopyNativeLevel_BD_to_RND(level);
7415   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7416     CopyNativeLevel_EM_to_RND(level);
7417   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7418     CopyNativeLevel_SP_to_RND(level);
7419   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7420     CopyNativeLevel_MM_to_RND(level);
7421 }
7422
7423 void SaveNativeLevel(struct LevelInfo *level)
7424 {
7425   // saving native level files only supported for some game engines
7426   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7427       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7428     return;
7429
7430   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7431                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7432   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7433   char *filename = getLevelFilenameFromBasename(basename);
7434
7435   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7436     return;
7437
7438   boolean success = FALSE;
7439
7440   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7441   {
7442     CopyNativeLevel_RND_to_BD(level);
7443     // CopyNativeTape_RND_to_BD(level);
7444
7445     success = SaveNativeLevel_BD(filename);
7446   }
7447   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7448   {
7449     CopyNativeLevel_RND_to_SP(level);
7450     CopyNativeTape_RND_to_SP(level);
7451
7452     success = SaveNativeLevel_SP(filename);
7453   }
7454
7455   if (success)
7456     Request("Native level file saved!", REQ_CONFIRM);
7457   else
7458     Request("Failed to save native level file!", REQ_CONFIRM);
7459 }
7460
7461
7462 // ----------------------------------------------------------------------------
7463 // functions for loading generic level
7464 // ----------------------------------------------------------------------------
7465
7466 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7467                                   struct LevelFileInfo *level_file_info,
7468                                   boolean level_info_only)
7469 {
7470   // always start with reliable default values
7471   setLevelInfoToDefaults(level, level_info_only, TRUE);
7472
7473   switch (level_file_info->type)
7474   {
7475     case LEVEL_FILE_TYPE_RND:
7476       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7477       break;
7478
7479     case LEVEL_FILE_TYPE_BD:
7480       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7481       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7482       break;
7483
7484     case LEVEL_FILE_TYPE_EM:
7485       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7486       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7487       break;
7488
7489     case LEVEL_FILE_TYPE_SP:
7490       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7491       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7492       break;
7493
7494     case LEVEL_FILE_TYPE_MM:
7495       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7496       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7497       break;
7498
7499     case LEVEL_FILE_TYPE_DC:
7500       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7501       break;
7502
7503     case LEVEL_FILE_TYPE_SB:
7504       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7505       break;
7506
7507     default:
7508       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7509       break;
7510   }
7511
7512   // if level file is invalid, restore level structure to default values
7513   if (level->no_valid_file)
7514     setLevelInfoToDefaults(level, level_info_only, FALSE);
7515
7516   if (check_special_flags("use_native_bd_game_engine"))
7517     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7518
7519   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7520     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7521
7522   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7523     CopyNativeLevel_Native_to_RND(level);
7524 }
7525
7526 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7527 {
7528   static struct LevelFileInfo level_file_info;
7529
7530   // always start with reliable default values
7531   setFileInfoToDefaults(&level_file_info);
7532
7533   level_file_info.nr = 0;                       // unknown level number
7534   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7535
7536   setString(&level_file_info.filename, filename);
7537
7538   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7539 }
7540
7541 static void LoadLevel_InitVersion(struct LevelInfo *level)
7542 {
7543   int i, j;
7544
7545   if (leveldir_current == NULL)         // only when dumping level
7546     return;
7547
7548   // all engine modifications also valid for levels which use latest engine
7549   if (level->game_version < VERSION_IDENT(3,2,0,5))
7550   {
7551     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7552     level->time_score_base = 10;
7553   }
7554
7555   if (leveldir_current->latest_engine)
7556   {
7557     // ---------- use latest game engine --------------------------------------
7558
7559     /* For all levels which are forced to use the latest game engine version
7560        (normally all but user contributed, private and undefined levels), set
7561        the game engine version to the actual version; this allows for actual
7562        corrections in the game engine to take effect for existing, converted
7563        levels (from "classic" or other existing games) to make the emulation
7564        of the corresponding game more accurate, while (hopefully) not breaking
7565        existing levels created from other players. */
7566
7567     level->game_version = GAME_VERSION_ACTUAL;
7568
7569     /* Set special EM style gems behaviour: EM style gems slip down from
7570        normal, steel and growing wall. As this is a more fundamental change,
7571        it seems better to set the default behaviour to "off" (as it is more
7572        natural) and make it configurable in the level editor (as a property
7573        of gem style elements). Already existing converted levels (neither
7574        private nor contributed levels) are changed to the new behaviour. */
7575
7576     if (level->file_version < FILE_VERSION_2_0)
7577       level->em_slippery_gems = TRUE;
7578
7579     return;
7580   }
7581
7582   // ---------- use game engine the level was created with --------------------
7583
7584   /* For all levels which are not forced to use the latest game engine
7585      version (normally user contributed, private and undefined levels),
7586      use the version of the game engine the levels were created for.
7587
7588      Since 2.0.1, the game engine version is now directly stored
7589      in the level file (chunk "VERS"), so there is no need anymore
7590      to set the game version from the file version (except for old,
7591      pre-2.0 levels, where the game version is still taken from the
7592      file format version used to store the level -- see above). */
7593
7594   // player was faster than enemies in 1.0.0 and before
7595   if (level->file_version == FILE_VERSION_1_0)
7596     for (i = 0; i < MAX_PLAYERS; i++)
7597       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7598
7599   // default behaviour for EM style gems was "slippery" only in 2.0.1
7600   if (level->game_version == VERSION_IDENT(2,0,1,0))
7601     level->em_slippery_gems = TRUE;
7602
7603   // springs could be pushed over pits before (pre-release version) 2.2.0
7604   if (level->game_version < VERSION_IDENT(2,2,0,0))
7605     level->use_spring_bug = TRUE;
7606
7607   if (level->game_version < VERSION_IDENT(3,2,0,5))
7608   {
7609     // time orb caused limited time in endless time levels before 3.2.0-5
7610     level->use_time_orb_bug = TRUE;
7611
7612     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7613     level->block_snap_field = FALSE;
7614
7615     // extra time score was same value as time left score before 3.2.0-5
7616     level->extra_time_score = level->score[SC_TIME_BONUS];
7617   }
7618
7619   if (level->game_version < VERSION_IDENT(3,2,0,7))
7620   {
7621     // default behaviour for snapping was "not continuous" before 3.2.0-7
7622     level->continuous_snapping = FALSE;
7623   }
7624
7625   // only few elements were able to actively move into acid before 3.1.0
7626   // trigger settings did not exist before 3.1.0; set to default "any"
7627   if (level->game_version < VERSION_IDENT(3,1,0,0))
7628   {
7629     // correct "can move into acid" settings (all zero in old levels)
7630
7631     level->can_move_into_acid_bits = 0; // nothing can move into acid
7632     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7633
7634     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7635     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7636     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7637     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7638
7639     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7640       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7641
7642     // correct trigger settings (stored as zero == "none" in old levels)
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         change->trigger_player = CH_PLAYER_ANY;
7654         change->trigger_page = CH_PAGE_ANY;
7655       }
7656     }
7657   }
7658
7659   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7660   {
7661     int element = EL_CUSTOM_256;
7662     struct ElementInfo *ei = &element_info[element];
7663     struct ElementChangeInfo *change = &ei->change_page[0];
7664
7665     /* This is needed to fix a problem that was caused by a bugfix in function
7666        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7667        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7668        not replace walkable elements, but instead just placed the player on it,
7669        without placing the Sokoban field under the player). Unfortunately, this
7670        breaks "Snake Bite" style levels when the snake is halfway through a door
7671        that just closes (the snake head is still alive and can be moved in this
7672        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7673        player (without Sokoban element) which then gets killed as designed). */
7674
7675     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7676          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7677         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7678       change->target_element = EL_PLAYER_1;
7679   }
7680
7681   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7682   if (level->game_version < VERSION_IDENT(3,2,5,0))
7683   {
7684     /* This is needed to fix a problem that was caused by a bugfix in function
7685        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7686        corrects the behaviour when a custom element changes to another custom
7687        element with a higher element number that has change actions defined.
7688        Normally, only one change per frame is allowed for custom elements.
7689        Therefore, it is checked if a custom element already changed in the
7690        current frame; if it did, subsequent changes are suppressed.
7691        Unfortunately, this is only checked for element changes, but not for
7692        change actions, which are still executed. As the function above loops
7693        through all custom elements from lower to higher, an element change
7694        resulting in a lower CE number won't be checked again, while a target
7695        element with a higher number will also be checked, and potential change
7696        actions will get executed for this CE, too (which is wrong), while
7697        further changes are ignored (which is correct). As this bugfix breaks
7698        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7699        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7700        behaviour for existing levels and tapes that make use of this bug */
7701
7702     level->use_action_after_change_bug = TRUE;
7703   }
7704
7705   // not centering level after relocating player was default only in 3.2.3
7706   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7707     level->shifted_relocation = TRUE;
7708
7709   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7710   if (level->game_version < VERSION_IDENT(3,2,6,0))
7711     level->em_explodes_by_fire = TRUE;
7712
7713   // levels were solved by the first player entering an exit up to 4.1.0.0
7714   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7715     level->solved_by_one_player = TRUE;
7716
7717   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7718   if (level->game_version < VERSION_IDENT(4,1,1,1))
7719     level->use_life_bugs = TRUE;
7720
7721   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7722   if (level->game_version < VERSION_IDENT(4,1,1,1))
7723     level->sb_objects_needed = FALSE;
7724
7725   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7726   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7727     level->finish_dig_collect = FALSE;
7728
7729   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7730   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7731     level->keep_walkable_ce = TRUE;
7732 }
7733
7734 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7735 {
7736   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7737   int x, y;
7738
7739   // check if this level is (not) a Sokoban level
7740   for (y = 0; y < level->fieldy; y++)
7741     for (x = 0; x < level->fieldx; x++)
7742       if (!IS_SB_ELEMENT(Tile[x][y]))
7743         is_sokoban_level = FALSE;
7744
7745   if (is_sokoban_level)
7746   {
7747     // set special level settings for Sokoban levels
7748     SetLevelSettings_SB(level);
7749   }
7750 }
7751
7752 static void LoadLevel_InitSettings(struct LevelInfo *level)
7753 {
7754   // adjust level settings for (non-native) Sokoban-style levels
7755   LoadLevel_InitSettings_SB(level);
7756
7757   // rename levels with title "nameless level" or if renaming is forced
7758   if (leveldir_current->empty_level_name != NULL &&
7759       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7760        leveldir_current->force_level_name))
7761     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7762              leveldir_current->empty_level_name, level_nr);
7763 }
7764
7765 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7766 {
7767   int i, x, y;
7768
7769   // map elements that have changed in newer versions
7770   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7771                                                     level->game_version);
7772   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7773     for (x = 0; x < 3; x++)
7774       for (y = 0; y < 3; y++)
7775         level->yamyam_content[i].e[x][y] =
7776           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7777                                     level->game_version);
7778
7779 }
7780
7781 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7782 {
7783   int i, j;
7784
7785   // map custom element change events that have changed in newer versions
7786   // (these following values were accidentally changed in version 3.0.1)
7787   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7788   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7789   {
7790     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7791     {
7792       int element = EL_CUSTOM_START + i;
7793
7794       // order of checking and copying events to be mapped is important
7795       // (do not change the start and end value -- they are constant)
7796       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7797       {
7798         if (HAS_CHANGE_EVENT(element, j - 2))
7799         {
7800           SET_CHANGE_EVENT(element, j - 2, FALSE);
7801           SET_CHANGE_EVENT(element, j, TRUE);
7802         }
7803       }
7804
7805       // order of checking and copying events to be mapped is important
7806       // (do not change the start and end value -- they are constant)
7807       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7808       {
7809         if (HAS_CHANGE_EVENT(element, j - 1))
7810         {
7811           SET_CHANGE_EVENT(element, j - 1, FALSE);
7812           SET_CHANGE_EVENT(element, j, TRUE);
7813         }
7814       }
7815     }
7816   }
7817
7818   // initialize "can_change" field for old levels with only one change page
7819   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7820   {
7821     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7822     {
7823       int element = EL_CUSTOM_START + i;
7824
7825       if (CAN_CHANGE(element))
7826         element_info[element].change->can_change = TRUE;
7827     }
7828   }
7829
7830   // correct custom element values (for old levels without these options)
7831   if (level->game_version < VERSION_IDENT(3,1,1,0))
7832   {
7833     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7834     {
7835       int element = EL_CUSTOM_START + i;
7836       struct ElementInfo *ei = &element_info[element];
7837
7838       if (ei->access_direction == MV_NO_DIRECTION)
7839         ei->access_direction = MV_ALL_DIRECTIONS;
7840     }
7841   }
7842
7843   // correct custom element values (fix invalid values for all versions)
7844   if (1)
7845   {
7846     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7847     {
7848       int element = EL_CUSTOM_START + i;
7849       struct ElementInfo *ei = &element_info[element];
7850
7851       for (j = 0; j < ei->num_change_pages; j++)
7852       {
7853         struct ElementChangeInfo *change = &ei->change_page[j];
7854
7855         if (change->trigger_player == CH_PLAYER_NONE)
7856           change->trigger_player = CH_PLAYER_ANY;
7857
7858         if (change->trigger_side == CH_SIDE_NONE)
7859           change->trigger_side = CH_SIDE_ANY;
7860       }
7861     }
7862   }
7863
7864   // initialize "can_explode" field for old levels which did not store this
7865   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7866   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7867   {
7868     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7869     {
7870       int element = EL_CUSTOM_START + i;
7871
7872       if (EXPLODES_1X1_OLD(element))
7873         element_info[element].explosion_type = EXPLODES_1X1;
7874
7875       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7876                                              EXPLODES_SMASHED(element) ||
7877                                              EXPLODES_IMPACT(element)));
7878     }
7879   }
7880
7881   // correct previously hard-coded move delay values for maze runner style
7882   if (level->game_version < VERSION_IDENT(3,1,1,0))
7883   {
7884     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7885     {
7886       int element = EL_CUSTOM_START + i;
7887
7888       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7889       {
7890         // previously hard-coded and therefore ignored
7891         element_info[element].move_delay_fixed = 9;
7892         element_info[element].move_delay_random = 0;
7893       }
7894     }
7895   }
7896
7897   // set some other uninitialized values of custom elements in older levels
7898   if (level->game_version < VERSION_IDENT(3,1,0,0))
7899   {
7900     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7901     {
7902       int element = EL_CUSTOM_START + i;
7903
7904       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7905
7906       element_info[element].explosion_delay = 17;
7907       element_info[element].ignition_delay = 8;
7908     }
7909   }
7910
7911   // set mouse click change events to work for left/middle/right mouse button
7912   if (level->game_version < VERSION_IDENT(4,2,3,0))
7913   {
7914     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7915     {
7916       int element = EL_CUSTOM_START + i;
7917       struct ElementInfo *ei = &element_info[element];
7918
7919       for (j = 0; j < ei->num_change_pages; j++)
7920       {
7921         struct ElementChangeInfo *change = &ei->change_page[j];
7922
7923         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7924             change->has_event[CE_PRESSED_BY_MOUSE] ||
7925             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7926             change->has_event[CE_MOUSE_PRESSED_ON_X])
7927           change->trigger_side = CH_SIDE_ANY;
7928       }
7929     }
7930   }
7931 }
7932
7933 static void LoadLevel_InitElements(struct LevelInfo *level)
7934 {
7935   LoadLevel_InitStandardElements(level);
7936
7937   if (level->file_has_custom_elements)
7938     LoadLevel_InitCustomElements(level);
7939
7940   // initialize element properties for level editor etc.
7941   InitElementPropertiesEngine(level->game_version);
7942   InitElementPropertiesGfxElement();
7943 }
7944
7945 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7946 {
7947   int x, y;
7948
7949   // map elements that have changed in newer versions
7950   for (y = 0; y < level->fieldy; y++)
7951     for (x = 0; x < level->fieldx; x++)
7952       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7953                                                      level->game_version);
7954
7955   // clear unused playfield data (nicer if level gets resized in editor)
7956   for (x = 0; x < MAX_LEV_FIELDX; x++)
7957     for (y = 0; y < MAX_LEV_FIELDY; y++)
7958       if (x >= level->fieldx || y >= level->fieldy)
7959         level->field[x][y] = EL_EMPTY;
7960
7961   // copy elements to runtime playfield array
7962   for (x = 0; x < MAX_LEV_FIELDX; x++)
7963     for (y = 0; y < MAX_LEV_FIELDY; y++)
7964       Tile[x][y] = level->field[x][y];
7965
7966   // initialize level size variables for faster access
7967   lev_fieldx = level->fieldx;
7968   lev_fieldy = level->fieldy;
7969
7970   // determine border element for this level
7971   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7972     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7973   else
7974     SetBorderElement();
7975 }
7976
7977 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7978 {
7979   struct LevelFileInfo *level_file_info = &level->file_info;
7980
7981   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7982     CopyNativeLevel_RND_to_Native(level);
7983 }
7984
7985 static void LoadLevelTemplate_LoadAndInit(void)
7986 {
7987   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7988
7989   LoadLevel_InitVersion(&level_template);
7990   LoadLevel_InitElements(&level_template);
7991   LoadLevel_InitSettings(&level_template);
7992
7993   ActivateLevelTemplate();
7994 }
7995
7996 void LoadLevelTemplate(int nr)
7997 {
7998   if (!fileExists(getGlobalLevelTemplateFilename()))
7999   {
8000     Warn("no level template found for this level");
8001
8002     return;
8003   }
8004
8005   setLevelFileInfo(&level_template.file_info, nr);
8006
8007   LoadLevelTemplate_LoadAndInit();
8008 }
8009
8010 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8011 {
8012   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8013
8014   LoadLevelTemplate_LoadAndInit();
8015 }
8016
8017 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8018 {
8019   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8020
8021   if (level.use_custom_template)
8022   {
8023     if (network_level != NULL)
8024       LoadNetworkLevelTemplate(network_level);
8025     else
8026       LoadLevelTemplate(-1);
8027   }
8028
8029   LoadLevel_InitVersion(&level);
8030   LoadLevel_InitElements(&level);
8031   LoadLevel_InitPlayfield(&level);
8032   LoadLevel_InitSettings(&level);
8033
8034   LoadLevel_InitNativeEngines(&level);
8035 }
8036
8037 void LoadLevel(int nr)
8038 {
8039   SetLevelSetInfo(leveldir_current->identifier, nr);
8040
8041   setLevelFileInfo(&level.file_info, nr);
8042
8043   LoadLevel_LoadAndInit(NULL);
8044 }
8045
8046 void LoadLevelInfoOnly(int nr)
8047 {
8048   setLevelFileInfo(&level.file_info, nr);
8049
8050   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8051 }
8052
8053 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8054 {
8055   SetLevelSetInfo(network_level->leveldir_identifier,
8056                   network_level->file_info.nr);
8057
8058   copyLevelFileInfo(&network_level->file_info, &level.file_info);
8059
8060   LoadLevel_LoadAndInit(network_level);
8061 }
8062
8063 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8064 {
8065   int chunk_size = 0;
8066
8067   chunk_size += putFileVersion(file, level->file_version);
8068   chunk_size += putFileVersion(file, level->game_version);
8069
8070   return chunk_size;
8071 }
8072
8073 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8074 {
8075   int chunk_size = 0;
8076
8077   chunk_size += putFile16BitBE(file, level->creation_date.year);
8078   chunk_size += putFile8Bit(file,    level->creation_date.month);
8079   chunk_size += putFile8Bit(file,    level->creation_date.day);
8080
8081   return chunk_size;
8082 }
8083
8084 #if ENABLE_HISTORIC_CHUNKS
8085 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8086 {
8087   int i, x, y;
8088
8089   putFile8Bit(file, level->fieldx);
8090   putFile8Bit(file, level->fieldy);
8091
8092   putFile16BitBE(file, level->time);
8093   putFile16BitBE(file, level->gems_needed);
8094
8095   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8096     putFile8Bit(file, level->name[i]);
8097
8098   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8099     putFile8Bit(file, level->score[i]);
8100
8101   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8102     for (y = 0; y < 3; y++)
8103       for (x = 0; x < 3; x++)
8104         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8105                            level->yamyam_content[i].e[x][y]));
8106   putFile8Bit(file, level->amoeba_speed);
8107   putFile8Bit(file, level->time_magic_wall);
8108   putFile8Bit(file, level->time_wheel);
8109   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8110                      level->amoeba_content));
8111   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8112   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8113   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8114   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8115
8116   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8117
8118   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8119   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8120   putFile32BitBE(file, level->can_move_into_acid_bits);
8121   putFile8Bit(file, level->dont_collide_with_bits);
8122
8123   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8124   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8125
8126   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8127   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8128   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8129
8130   putFile8Bit(file, level->game_engine_type);
8131
8132   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8133 }
8134 #endif
8135
8136 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8137 {
8138   int chunk_size = 0;
8139   int i;
8140
8141   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8142     chunk_size += putFile8Bit(file, level->name[i]);
8143
8144   return chunk_size;
8145 }
8146
8147 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8148 {
8149   int chunk_size = 0;
8150   int i;
8151
8152   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8153     chunk_size += putFile8Bit(file, level->author[i]);
8154
8155   return chunk_size;
8156 }
8157
8158 #if ENABLE_HISTORIC_CHUNKS
8159 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8160 {
8161   int chunk_size = 0;
8162   int x, y;
8163
8164   for (y = 0; y < level->fieldy; y++)
8165     for (x = 0; x < level->fieldx; x++)
8166       if (level->encoding_16bit_field)
8167         chunk_size += putFile16BitBE(file, level->field[x][y]);
8168       else
8169         chunk_size += putFile8Bit(file, level->field[x][y]);
8170
8171   return chunk_size;
8172 }
8173 #endif
8174
8175 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8176 {
8177   int chunk_size = 0;
8178   int x, y;
8179
8180   for (y = 0; y < level->fieldy; y++) 
8181     for (x = 0; x < level->fieldx; x++) 
8182       chunk_size += putFile16BitBE(file, level->field[x][y]);
8183
8184   return chunk_size;
8185 }
8186
8187 #if ENABLE_HISTORIC_CHUNKS
8188 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8189 {
8190   int i, x, y;
8191
8192   putFile8Bit(file, EL_YAMYAM);
8193   putFile8Bit(file, level->num_yamyam_contents);
8194   putFile8Bit(file, 0);
8195   putFile8Bit(file, 0);
8196
8197   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8198     for (y = 0; y < 3; y++)
8199       for (x = 0; x < 3; x++)
8200         if (level->encoding_16bit_field)
8201           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8202         else
8203           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8204 }
8205 #endif
8206
8207 #if ENABLE_HISTORIC_CHUNKS
8208 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8209 {
8210   int i, x, y;
8211   int num_contents, content_xsize, content_ysize;
8212   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8213
8214   if (element == EL_YAMYAM)
8215   {
8216     num_contents = level->num_yamyam_contents;
8217     content_xsize = 3;
8218     content_ysize = 3;
8219
8220     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8221       for (y = 0; y < 3; y++)
8222         for (x = 0; x < 3; x++)
8223           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8224   }
8225   else if (element == EL_BD_AMOEBA)
8226   {
8227     num_contents = 1;
8228     content_xsize = 1;
8229     content_ysize = 1;
8230
8231     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8232       for (y = 0; y < 3; y++)
8233         for (x = 0; x < 3; x++)
8234           content_array[i][x][y] = EL_EMPTY;
8235     content_array[0][0][0] = level->amoeba_content;
8236   }
8237   else
8238   {
8239     // chunk header already written -- write empty chunk data
8240     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8241
8242     Warn("cannot save content for element '%d'", element);
8243
8244     return;
8245   }
8246
8247   putFile16BitBE(file, element);
8248   putFile8Bit(file, num_contents);
8249   putFile8Bit(file, content_xsize);
8250   putFile8Bit(file, content_ysize);
8251
8252   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8253
8254   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8255     for (y = 0; y < 3; y++)
8256       for (x = 0; x < 3; x++)
8257         putFile16BitBE(file, content_array[i][x][y]);
8258 }
8259 #endif
8260
8261 #if ENABLE_HISTORIC_CHUNKS
8262 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8263 {
8264   int envelope_nr = element - EL_ENVELOPE_1;
8265   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8266   int chunk_size = 0;
8267   int i;
8268
8269   chunk_size += putFile16BitBE(file, element);
8270   chunk_size += putFile16BitBE(file, envelope_len);
8271   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8272   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8273
8274   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8275   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8276
8277   for (i = 0; i < envelope_len; i++)
8278     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8279
8280   return chunk_size;
8281 }
8282 #endif
8283
8284 #if ENABLE_HISTORIC_CHUNKS
8285 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8286                            int num_changed_custom_elements)
8287 {
8288   int i, check = 0;
8289
8290   putFile16BitBE(file, num_changed_custom_elements);
8291
8292   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8293   {
8294     int element = EL_CUSTOM_START + i;
8295
8296     struct ElementInfo *ei = &element_info[element];
8297
8298     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8299     {
8300       if (check < num_changed_custom_elements)
8301       {
8302         putFile16BitBE(file, element);
8303         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8304       }
8305
8306       check++;
8307     }
8308   }
8309
8310   if (check != num_changed_custom_elements)     // should not happen
8311     Warn("inconsistent number of custom element properties");
8312 }
8313 #endif
8314
8315 #if ENABLE_HISTORIC_CHUNKS
8316 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8317                            int num_changed_custom_elements)
8318 {
8319   int i, check = 0;
8320
8321   putFile16BitBE(file, num_changed_custom_elements);
8322
8323   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8324   {
8325     int element = EL_CUSTOM_START + i;
8326
8327     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8328     {
8329       if (check < num_changed_custom_elements)
8330       {
8331         putFile16BitBE(file, element);
8332         putFile16BitBE(file, element_info[element].change->target_element);
8333       }
8334
8335       check++;
8336     }
8337   }
8338
8339   if (check != num_changed_custom_elements)     // should not happen
8340     Warn("inconsistent number of custom target elements");
8341 }
8342 #endif
8343
8344 #if ENABLE_HISTORIC_CHUNKS
8345 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8346                            int num_changed_custom_elements)
8347 {
8348   int i, j, x, y, check = 0;
8349
8350   putFile16BitBE(file, num_changed_custom_elements);
8351
8352   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8353   {
8354     int element = EL_CUSTOM_START + i;
8355     struct ElementInfo *ei = &element_info[element];
8356
8357     if (ei->modified_settings)
8358     {
8359       if (check < num_changed_custom_elements)
8360       {
8361         putFile16BitBE(file, element);
8362
8363         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8364           putFile8Bit(file, ei->description[j]);
8365
8366         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8367
8368         // some free bytes for future properties and padding
8369         WriteUnusedBytesToFile(file, 7);
8370
8371         putFile8Bit(file, ei->use_gfx_element);
8372         putFile16BitBE(file, ei->gfx_element_initial);
8373
8374         putFile8Bit(file, ei->collect_score_initial);
8375         putFile8Bit(file, ei->collect_count_initial);
8376
8377         putFile16BitBE(file, ei->push_delay_fixed);
8378         putFile16BitBE(file, ei->push_delay_random);
8379         putFile16BitBE(file, ei->move_delay_fixed);
8380         putFile16BitBE(file, ei->move_delay_random);
8381
8382         putFile16BitBE(file, ei->move_pattern);
8383         putFile8Bit(file, ei->move_direction_initial);
8384         putFile8Bit(file, ei->move_stepsize);
8385
8386         for (y = 0; y < 3; y++)
8387           for (x = 0; x < 3; x++)
8388             putFile16BitBE(file, ei->content.e[x][y]);
8389
8390         putFile32BitBE(file, ei->change->events);
8391
8392         putFile16BitBE(file, ei->change->target_element);
8393
8394         putFile16BitBE(file, ei->change->delay_fixed);
8395         putFile16BitBE(file, ei->change->delay_random);
8396         putFile16BitBE(file, ei->change->delay_frames);
8397
8398         putFile16BitBE(file, ei->change->initial_trigger_element);
8399
8400         putFile8Bit(file, ei->change->explode);
8401         putFile8Bit(file, ei->change->use_target_content);
8402         putFile8Bit(file, ei->change->only_if_complete);
8403         putFile8Bit(file, ei->change->use_random_replace);
8404
8405         putFile8Bit(file, ei->change->random_percentage);
8406         putFile8Bit(file, ei->change->replace_when);
8407
8408         for (y = 0; y < 3; y++)
8409           for (x = 0; x < 3; x++)
8410             putFile16BitBE(file, ei->change->content.e[x][y]);
8411
8412         putFile8Bit(file, ei->slippery_type);
8413
8414         // some free bytes for future properties and padding
8415         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8416       }
8417
8418       check++;
8419     }
8420   }
8421
8422   if (check != num_changed_custom_elements)     // should not happen
8423     Warn("inconsistent number of custom element properties");
8424 }
8425 #endif
8426
8427 #if ENABLE_HISTORIC_CHUNKS
8428 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8429 {
8430   struct ElementInfo *ei = &element_info[element];
8431   int i, j, x, y;
8432
8433   // ---------- custom element base property values (96 bytes) ----------------
8434
8435   putFile16BitBE(file, element);
8436
8437   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8438     putFile8Bit(file, ei->description[i]);
8439
8440   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8441
8442   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8443
8444   putFile8Bit(file, ei->num_change_pages);
8445
8446   putFile16BitBE(file, ei->ce_value_fixed_initial);
8447   putFile16BitBE(file, ei->ce_value_random_initial);
8448   putFile8Bit(file, ei->use_last_ce_value);
8449
8450   putFile8Bit(file, ei->use_gfx_element);
8451   putFile16BitBE(file, ei->gfx_element_initial);
8452
8453   putFile8Bit(file, ei->collect_score_initial);
8454   putFile8Bit(file, ei->collect_count_initial);
8455
8456   putFile8Bit(file, ei->drop_delay_fixed);
8457   putFile8Bit(file, ei->push_delay_fixed);
8458   putFile8Bit(file, ei->drop_delay_random);
8459   putFile8Bit(file, ei->push_delay_random);
8460   putFile16BitBE(file, ei->move_delay_fixed);
8461   putFile16BitBE(file, ei->move_delay_random);
8462
8463   // bits 0 - 15 of "move_pattern" ...
8464   putFile16BitBE(file, ei->move_pattern & 0xffff);
8465   putFile8Bit(file, ei->move_direction_initial);
8466   putFile8Bit(file, ei->move_stepsize);
8467
8468   putFile8Bit(file, ei->slippery_type);
8469
8470   for (y = 0; y < 3; y++)
8471     for (x = 0; x < 3; x++)
8472       putFile16BitBE(file, ei->content.e[x][y]);
8473
8474   putFile16BitBE(file, ei->move_enter_element);
8475   putFile16BitBE(file, ei->move_leave_element);
8476   putFile8Bit(file, ei->move_leave_type);
8477
8478   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8479   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8480
8481   putFile8Bit(file, ei->access_direction);
8482
8483   putFile8Bit(file, ei->explosion_delay);
8484   putFile8Bit(file, ei->ignition_delay);
8485   putFile8Bit(file, ei->explosion_type);
8486
8487   // some free bytes for future custom property values and padding
8488   WriteUnusedBytesToFile(file, 1);
8489
8490   // ---------- change page property values (48 bytes) ------------------------
8491
8492   for (i = 0; i < ei->num_change_pages; i++)
8493   {
8494     struct ElementChangeInfo *change = &ei->change_page[i];
8495     unsigned int event_bits;
8496
8497     // bits 0 - 31 of "has_event[]" ...
8498     event_bits = 0;
8499     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8500       if (change->has_event[j])
8501         event_bits |= (1u << j);
8502     putFile32BitBE(file, event_bits);
8503
8504     putFile16BitBE(file, change->target_element);
8505
8506     putFile16BitBE(file, change->delay_fixed);
8507     putFile16BitBE(file, change->delay_random);
8508     putFile16BitBE(file, change->delay_frames);
8509
8510     putFile16BitBE(file, change->initial_trigger_element);
8511
8512     putFile8Bit(file, change->explode);
8513     putFile8Bit(file, change->use_target_content);
8514     putFile8Bit(file, change->only_if_complete);
8515     putFile8Bit(file, change->use_random_replace);
8516
8517     putFile8Bit(file, change->random_percentage);
8518     putFile8Bit(file, change->replace_when);
8519
8520     for (y = 0; y < 3; y++)
8521       for (x = 0; x < 3; x++)
8522         putFile16BitBE(file, change->target_content.e[x][y]);
8523
8524     putFile8Bit(file, change->can_change);
8525
8526     putFile8Bit(file, change->trigger_side);
8527
8528     putFile8Bit(file, change->trigger_player);
8529     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8530                        log_2(change->trigger_page)));
8531
8532     putFile8Bit(file, change->has_action);
8533     putFile8Bit(file, change->action_type);
8534     putFile8Bit(file, change->action_mode);
8535     putFile16BitBE(file, change->action_arg);
8536
8537     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8538     event_bits = 0;
8539     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8540       if (change->has_event[j])
8541         event_bits |= (1u << (j - 32));
8542     putFile8Bit(file, event_bits);
8543   }
8544 }
8545 #endif
8546
8547 #if ENABLE_HISTORIC_CHUNKS
8548 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8549 {
8550   struct ElementInfo *ei = &element_info[element];
8551   struct ElementGroupInfo *group = ei->group;
8552   int i;
8553
8554   putFile16BitBE(file, element);
8555
8556   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8557     putFile8Bit(file, ei->description[i]);
8558
8559   putFile8Bit(file, group->num_elements);
8560
8561   putFile8Bit(file, ei->use_gfx_element);
8562   putFile16BitBE(file, ei->gfx_element_initial);
8563
8564   putFile8Bit(file, group->choice_mode);
8565
8566   // some free bytes for future values and padding
8567   WriteUnusedBytesToFile(file, 3);
8568
8569   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8570     putFile16BitBE(file, group->element[i]);
8571 }
8572 #endif
8573
8574 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8575                                 boolean write_element)
8576 {
8577   int save_type = entry->save_type;
8578   int data_type = entry->data_type;
8579   int conf_type = entry->conf_type;
8580   int byte_mask = conf_type & CONF_MASK_BYTES;
8581   int element = entry->element;
8582   int default_value = entry->default_value;
8583   int num_bytes = 0;
8584   boolean modified = FALSE;
8585
8586   if (byte_mask != CONF_MASK_MULTI_BYTES)
8587   {
8588     void *value_ptr = entry->value;
8589     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8590                  *(int *)value_ptr);
8591
8592     // check if any settings have been modified before saving them
8593     if (value != default_value)
8594       modified = TRUE;
8595
8596     // do not save if explicitly told or if unmodified default settings
8597     if ((save_type == SAVE_CONF_NEVER) ||
8598         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8599       return 0;
8600
8601     if (write_element)
8602       num_bytes += putFile16BitBE(file, element);
8603
8604     num_bytes += putFile8Bit(file, conf_type);
8605     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8606                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8607                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8608                   0);
8609   }
8610   else if (data_type == TYPE_STRING)
8611   {
8612     char *default_string = entry->default_string;
8613     char *string = (char *)(entry->value);
8614     int string_length = strlen(string);
8615     int i;
8616
8617     // check if any settings have been modified before saving them
8618     if (!strEqual(string, default_string))
8619       modified = TRUE;
8620
8621     // do not save if explicitly told or if unmodified default settings
8622     if ((save_type == SAVE_CONF_NEVER) ||
8623         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8624       return 0;
8625
8626     if (write_element)
8627       num_bytes += putFile16BitBE(file, element);
8628
8629     num_bytes += putFile8Bit(file, conf_type);
8630     num_bytes += putFile16BitBE(file, string_length);
8631
8632     for (i = 0; i < string_length; i++)
8633       num_bytes += putFile8Bit(file, string[i]);
8634   }
8635   else if (data_type == TYPE_ELEMENT_LIST)
8636   {
8637     int *element_array = (int *)(entry->value);
8638     int num_elements = *(int *)(entry->num_entities);
8639     int i;
8640
8641     // check if any settings have been modified before saving them
8642     for (i = 0; i < num_elements; i++)
8643       if (element_array[i] != default_value)
8644         modified = TRUE;
8645
8646     // do not save if explicitly told or if unmodified default settings
8647     if ((save_type == SAVE_CONF_NEVER) ||
8648         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8649       return 0;
8650
8651     if (write_element)
8652       num_bytes += putFile16BitBE(file, element);
8653
8654     num_bytes += putFile8Bit(file, conf_type);
8655     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8656
8657     for (i = 0; i < num_elements; i++)
8658       num_bytes += putFile16BitBE(file, element_array[i]);
8659   }
8660   else if (data_type == TYPE_CONTENT_LIST)
8661   {
8662     struct Content *content = (struct Content *)(entry->value);
8663     int num_contents = *(int *)(entry->num_entities);
8664     int i, x, y;
8665
8666     // check if any settings have been modified before saving them
8667     for (i = 0; i < num_contents; i++)
8668       for (y = 0; y < 3; y++)
8669         for (x = 0; x < 3; x++)
8670           if (content[i].e[x][y] != default_value)
8671             modified = TRUE;
8672
8673     // do not save if explicitly told or if unmodified default settings
8674     if ((save_type == SAVE_CONF_NEVER) ||
8675         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8676       return 0;
8677
8678     if (write_element)
8679       num_bytes += putFile16BitBE(file, element);
8680
8681     num_bytes += putFile8Bit(file, conf_type);
8682     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8683
8684     for (i = 0; i < num_contents; i++)
8685       for (y = 0; y < 3; y++)
8686         for (x = 0; x < 3; x++)
8687           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8688   }
8689
8690   return num_bytes;
8691 }
8692
8693 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8694 {
8695   int chunk_size = 0;
8696   int i;
8697
8698   li = *level;          // copy level data into temporary buffer
8699
8700   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8701     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8702
8703   return chunk_size;
8704 }
8705
8706 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8707 {
8708   int chunk_size = 0;
8709   int i;
8710
8711   li = *level;          // copy level data into temporary buffer
8712
8713   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8714     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8715
8716   return chunk_size;
8717 }
8718
8719 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8720 {
8721   int envelope_nr = element - EL_ENVELOPE_1;
8722   int chunk_size = 0;
8723   int i;
8724
8725   chunk_size += putFile16BitBE(file, element);
8726
8727   // copy envelope data into temporary buffer
8728   xx_envelope = level->envelope[envelope_nr];
8729
8730   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8731     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8732
8733   return chunk_size;
8734 }
8735
8736 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8737 {
8738   struct ElementInfo *ei = &element_info[element];
8739   int chunk_size = 0;
8740   int i, j;
8741
8742   chunk_size += putFile16BitBE(file, element);
8743
8744   xx_ei = *ei;          // copy element data into temporary buffer
8745
8746   // set default description string for this specific element
8747   strcpy(xx_default_description, getDefaultElementDescription(ei));
8748
8749   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8750     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8751
8752   for (i = 0; i < ei->num_change_pages; i++)
8753   {
8754     struct ElementChangeInfo *change = &ei->change_page[i];
8755
8756     xx_current_change_page = i;
8757
8758     xx_change = *change;        // copy change data into temporary buffer
8759
8760     resetEventBits();
8761     setEventBitsFromEventFlags(change);
8762
8763     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8764       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8765                                          FALSE);
8766   }
8767
8768   return chunk_size;
8769 }
8770
8771 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8772 {
8773   struct ElementInfo *ei = &element_info[element];
8774   struct ElementGroupInfo *group = ei->group;
8775   int chunk_size = 0;
8776   int i;
8777
8778   chunk_size += putFile16BitBE(file, element);
8779
8780   xx_ei = *ei;          // copy element data into temporary buffer
8781   xx_group = *group;    // copy group data into temporary buffer
8782
8783   // set default description string for this specific element
8784   strcpy(xx_default_description, getDefaultElementDescription(ei));
8785
8786   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8787     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8788
8789   return chunk_size;
8790 }
8791
8792 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8793 {
8794   struct ElementInfo *ei = &element_info[element];
8795   int chunk_size = 0;
8796   int i;
8797
8798   chunk_size += putFile16BitBE(file, element);
8799
8800   xx_ei = *ei;          // copy element data into temporary buffer
8801
8802   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8803     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8804
8805   return chunk_size;
8806 }
8807
8808 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8809                                   boolean save_as_template)
8810 {
8811   int chunk_size;
8812   int i;
8813   FILE *file;
8814
8815   if (!(file = fopen(filename, MODE_WRITE)))
8816   {
8817     Warn("cannot save level file '%s'", filename);
8818
8819     return;
8820   }
8821
8822   level->file_version = FILE_VERSION_ACTUAL;
8823   level->game_version = GAME_VERSION_ACTUAL;
8824
8825   level->creation_date = getCurrentDate();
8826
8827   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8828   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8829
8830   chunk_size = SaveLevel_VERS(NULL, level);
8831   putFileChunkBE(file, "VERS", chunk_size);
8832   SaveLevel_VERS(file, level);
8833
8834   chunk_size = SaveLevel_DATE(NULL, level);
8835   putFileChunkBE(file, "DATE", chunk_size);
8836   SaveLevel_DATE(file, level);
8837
8838   chunk_size = SaveLevel_NAME(NULL, level);
8839   putFileChunkBE(file, "NAME", chunk_size);
8840   SaveLevel_NAME(file, level);
8841
8842   chunk_size = SaveLevel_AUTH(NULL, level);
8843   putFileChunkBE(file, "AUTH", chunk_size);
8844   SaveLevel_AUTH(file, level);
8845
8846   chunk_size = SaveLevel_INFO(NULL, level);
8847   putFileChunkBE(file, "INFO", chunk_size);
8848   SaveLevel_INFO(file, level);
8849
8850   chunk_size = SaveLevel_BODY(NULL, level);
8851   putFileChunkBE(file, "BODY", chunk_size);
8852   SaveLevel_BODY(file, level);
8853
8854   chunk_size = SaveLevel_ELEM(NULL, level);
8855   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8856   {
8857     putFileChunkBE(file, "ELEM", chunk_size);
8858     SaveLevel_ELEM(file, level);
8859   }
8860
8861   for (i = 0; i < NUM_ENVELOPES; i++)
8862   {
8863     int element = EL_ENVELOPE_1 + i;
8864
8865     chunk_size = SaveLevel_NOTE(NULL, level, element);
8866     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8867     {
8868       putFileChunkBE(file, "NOTE", chunk_size);
8869       SaveLevel_NOTE(file, level, element);
8870     }
8871   }
8872
8873   // if not using template level, check for non-default custom/group elements
8874   if (!level->use_custom_template || save_as_template)
8875   {
8876     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8877     {
8878       int element = EL_CUSTOM_START + i;
8879
8880       chunk_size = SaveLevel_CUSX(NULL, level, element);
8881       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8882       {
8883         putFileChunkBE(file, "CUSX", chunk_size);
8884         SaveLevel_CUSX(file, level, element);
8885       }
8886     }
8887
8888     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8889     {
8890       int element = EL_GROUP_START + i;
8891
8892       chunk_size = SaveLevel_GRPX(NULL, level, element);
8893       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8894       {
8895         putFileChunkBE(file, "GRPX", chunk_size);
8896         SaveLevel_GRPX(file, level, element);
8897       }
8898     }
8899
8900     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8901     {
8902       int element = GET_EMPTY_ELEMENT(i);
8903
8904       chunk_size = SaveLevel_EMPX(NULL, level, element);
8905       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8906       {
8907         putFileChunkBE(file, "EMPX", chunk_size);
8908         SaveLevel_EMPX(file, level, element);
8909       }
8910     }
8911   }
8912
8913   fclose(file);
8914
8915   SetFilePermissions(filename, PERMS_PRIVATE);
8916 }
8917
8918 void SaveLevel(int nr)
8919 {
8920   char *filename = getDefaultLevelFilename(nr);
8921
8922   SaveLevelFromFilename(&level, filename, FALSE);
8923 }
8924
8925 void SaveLevelTemplate(void)
8926 {
8927   char *filename = getLocalLevelTemplateFilename();
8928
8929   SaveLevelFromFilename(&level, filename, TRUE);
8930 }
8931
8932 boolean SaveLevelChecked(int nr)
8933 {
8934   char *filename = getDefaultLevelFilename(nr);
8935   boolean new_level = !fileExists(filename);
8936   boolean level_saved = FALSE;
8937
8938   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8939   {
8940     SaveLevel(nr);
8941
8942     if (new_level)
8943       Request("Level saved!", REQ_CONFIRM);
8944
8945     level_saved = TRUE;
8946   }
8947
8948   return level_saved;
8949 }
8950
8951 void DumpLevel(struct LevelInfo *level)
8952 {
8953   if (level->no_level_file || level->no_valid_file)
8954   {
8955     Warn("cannot dump -- no valid level file found");
8956
8957     return;
8958   }
8959
8960   PrintLine("-", 79);
8961   Print("Level xxx (file version %08d, game version %08d)\n",
8962         level->file_version, level->game_version);
8963   PrintLine("-", 79);
8964
8965   Print("Level author: '%s'\n", level->author);
8966   Print("Level title:  '%s'\n", level->name);
8967   Print("\n");
8968   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8969   Print("\n");
8970   Print("Level time:  %d seconds\n", level->time);
8971   Print("Gems needed: %d\n", level->gems_needed);
8972   Print("\n");
8973   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8974   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8975   Print("Time for light:      %d seconds\n", level->time_light);
8976   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8977   Print("\n");
8978   Print("Amoeba speed: %d\n", level->amoeba_speed);
8979   Print("\n");
8980
8981   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8982   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8983   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8984   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8985   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8986   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8987
8988   if (options.debug)
8989   {
8990     int i, j;
8991
8992     for (i = 0; i < NUM_ENVELOPES; i++)
8993     {
8994       char *text = level->envelope[i].text;
8995       int text_len = strlen(text);
8996       boolean has_text = FALSE;
8997
8998       for (j = 0; j < text_len; j++)
8999         if (text[j] != ' ' && text[j] != '\n')
9000           has_text = TRUE;
9001
9002       if (has_text)
9003       {
9004         Print("\n");
9005         Print("Envelope %d:\n'%s'\n", i + 1, text);
9006       }
9007     }
9008   }
9009
9010   PrintLine("-", 79);
9011 }
9012
9013 void DumpLevels(void)
9014 {
9015   static LevelDirTree *dumplevel_leveldir = NULL;
9016
9017   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9018                                                  global.dumplevel_leveldir);
9019
9020   if (dumplevel_leveldir == NULL)
9021     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9022
9023   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9024       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9025     Fail("no such level number: %d", global.dumplevel_level_nr);
9026
9027   leveldir_current = dumplevel_leveldir;
9028
9029   LoadLevel(global.dumplevel_level_nr);
9030   DumpLevel(&level);
9031
9032   CloseAllAndExit(0);
9033 }
9034
9035
9036 // ============================================================================
9037 // tape file functions
9038 // ============================================================================
9039
9040 static void setTapeInfoToDefaults(void)
9041 {
9042   int i;
9043
9044   // always start with reliable default values (empty tape)
9045   TapeErase();
9046
9047   // default values (also for pre-1.2 tapes) with only the first player
9048   tape.player_participates[0] = TRUE;
9049   for (i = 1; i < MAX_PLAYERS; i++)
9050     tape.player_participates[i] = FALSE;
9051
9052   // at least one (default: the first) player participates in every tape
9053   tape.num_participating_players = 1;
9054
9055   tape.property_bits = TAPE_PROPERTY_NONE;
9056
9057   tape.level_nr = level_nr;
9058   tape.counter = 0;
9059   tape.changed = FALSE;
9060   tape.solved = FALSE;
9061
9062   tape.recording = FALSE;
9063   tape.playing = FALSE;
9064   tape.pausing = FALSE;
9065
9066   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9067   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9068
9069   tape.no_info_chunk = TRUE;
9070   tape.no_valid_file = FALSE;
9071 }
9072
9073 static int getTapePosSize(struct TapeInfo *tape)
9074 {
9075   int tape_pos_size = 0;
9076
9077   if (tape->use_key_actions)
9078     tape_pos_size += tape->num_participating_players;
9079
9080   if (tape->use_mouse_actions)
9081     tape_pos_size += 3;         // x and y position and mouse button mask
9082
9083   tape_pos_size += 1;           // tape action delay value
9084
9085   return tape_pos_size;
9086 }
9087
9088 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9089 {
9090   tape->use_key_actions = FALSE;
9091   tape->use_mouse_actions = FALSE;
9092
9093   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9094     tape->use_key_actions = TRUE;
9095
9096   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9097     tape->use_mouse_actions = TRUE;
9098 }
9099
9100 static int getTapeActionValue(struct TapeInfo *tape)
9101 {
9102   return (tape->use_key_actions &&
9103           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9104           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9105           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9106           TAPE_ACTIONS_DEFAULT);
9107 }
9108
9109 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9110 {
9111   tape->file_version = getFileVersion(file);
9112   tape->game_version = getFileVersion(file);
9113
9114   return chunk_size;
9115 }
9116
9117 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9118 {
9119   int i;
9120
9121   tape->random_seed = getFile32BitBE(file);
9122   tape->date        = getFile32BitBE(file);
9123   tape->length      = getFile32BitBE(file);
9124
9125   // read header fields that are new since version 1.2
9126   if (tape->file_version >= FILE_VERSION_1_2)
9127   {
9128     byte store_participating_players = getFile8Bit(file);
9129     int engine_version;
9130
9131     // since version 1.2, tapes store which players participate in the tape
9132     tape->num_participating_players = 0;
9133     for (i = 0; i < MAX_PLAYERS; i++)
9134     {
9135       tape->player_participates[i] = FALSE;
9136
9137       if (store_participating_players & (1 << i))
9138       {
9139         tape->player_participates[i] = TRUE;
9140         tape->num_participating_players++;
9141       }
9142     }
9143
9144     setTapeActionFlags(tape, getFile8Bit(file));
9145
9146     tape->property_bits = getFile8Bit(file);
9147     tape->solved = getFile8Bit(file);
9148
9149     engine_version = getFileVersion(file);
9150     if (engine_version > 0)
9151       tape->engine_version = engine_version;
9152     else
9153       tape->engine_version = tape->game_version;
9154   }
9155
9156   return chunk_size;
9157 }
9158
9159 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9160 {
9161   tape->scr_fieldx = getFile8Bit(file);
9162   tape->scr_fieldy = getFile8Bit(file);
9163
9164   return chunk_size;
9165 }
9166
9167 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9168 {
9169   char *level_identifier = NULL;
9170   int level_identifier_size;
9171   int i;
9172
9173   tape->no_info_chunk = FALSE;
9174
9175   level_identifier_size = getFile16BitBE(file);
9176
9177   level_identifier = checked_malloc(level_identifier_size);
9178
9179   for (i = 0; i < level_identifier_size; i++)
9180     level_identifier[i] = getFile8Bit(file);
9181
9182   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9183   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9184
9185   checked_free(level_identifier);
9186
9187   tape->level_nr = getFile16BitBE(file);
9188
9189   chunk_size = 2 + level_identifier_size + 2;
9190
9191   return chunk_size;
9192 }
9193
9194 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9195 {
9196   int i, j;
9197   int tape_pos_size = getTapePosSize(tape);
9198   int chunk_size_expected = tape_pos_size * tape->length;
9199
9200   if (chunk_size_expected != chunk_size)
9201   {
9202     ReadUnusedBytesFromFile(file, chunk_size);
9203     return chunk_size_expected;
9204   }
9205
9206   for (i = 0; i < tape->length; i++)
9207   {
9208     if (i >= MAX_TAPE_LEN)
9209     {
9210       Warn("tape truncated -- size exceeds maximum tape size %d",
9211             MAX_TAPE_LEN);
9212
9213       // tape too large; read and ignore remaining tape data from this chunk
9214       for (;i < tape->length; i++)
9215         ReadUnusedBytesFromFile(file, tape_pos_size);
9216
9217       break;
9218     }
9219
9220     if (tape->use_key_actions)
9221     {
9222       for (j = 0; j < MAX_PLAYERS; j++)
9223       {
9224         tape->pos[i].action[j] = MV_NONE;
9225
9226         if (tape->player_participates[j])
9227           tape->pos[i].action[j] = getFile8Bit(file);
9228       }
9229     }
9230
9231     if (tape->use_mouse_actions)
9232     {
9233       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9234       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9235       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9236     }
9237
9238     tape->pos[i].delay = getFile8Bit(file);
9239
9240     if (tape->file_version == FILE_VERSION_1_0)
9241     {
9242       // eliminate possible diagonal moves in old tapes
9243       // this is only for backward compatibility
9244
9245       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9246       byte action = tape->pos[i].action[0];
9247       int k, num_moves = 0;
9248
9249       for (k = 0; k < 4; k++)
9250       {
9251         if (action & joy_dir[k])
9252         {
9253           tape->pos[i + num_moves].action[0] = joy_dir[k];
9254           if (num_moves > 0)
9255             tape->pos[i + num_moves].delay = 0;
9256           num_moves++;
9257         }
9258       }
9259
9260       if (num_moves > 1)
9261       {
9262         num_moves--;
9263         i += num_moves;
9264         tape->length += num_moves;
9265       }
9266     }
9267     else if (tape->file_version < FILE_VERSION_2_0)
9268     {
9269       // convert pre-2.0 tapes to new tape format
9270
9271       if (tape->pos[i].delay > 1)
9272       {
9273         // action part
9274         tape->pos[i + 1] = tape->pos[i];
9275         tape->pos[i + 1].delay = 1;
9276
9277         // delay part
9278         for (j = 0; j < MAX_PLAYERS; j++)
9279           tape->pos[i].action[j] = MV_NONE;
9280         tape->pos[i].delay--;
9281
9282         i++;
9283         tape->length++;
9284       }
9285     }
9286
9287     if (checkEndOfFile(file))
9288       break;
9289   }
9290
9291   if (i != tape->length)
9292     chunk_size = tape_pos_size * i;
9293
9294   return chunk_size;
9295 }
9296
9297 static void LoadTape_SokobanSolution(char *filename)
9298 {
9299   File *file;
9300   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9301
9302   if (!(file = openFile(filename, MODE_READ)))
9303   {
9304     tape.no_valid_file = TRUE;
9305
9306     return;
9307   }
9308
9309   while (!checkEndOfFile(file))
9310   {
9311     unsigned char c = getByteFromFile(file);
9312
9313     if (checkEndOfFile(file))
9314       break;
9315
9316     switch (c)
9317     {
9318       case 'u':
9319       case 'U':
9320         tape.pos[tape.length].action[0] = MV_UP;
9321         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9322         tape.length++;
9323         break;
9324
9325       case 'd':
9326       case 'D':
9327         tape.pos[tape.length].action[0] = MV_DOWN;
9328         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9329         tape.length++;
9330         break;
9331
9332       case 'l':
9333       case 'L':
9334         tape.pos[tape.length].action[0] = MV_LEFT;
9335         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9336         tape.length++;
9337         break;
9338
9339       case 'r':
9340       case 'R':
9341         tape.pos[tape.length].action[0] = MV_RIGHT;
9342         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9343         tape.length++;
9344         break;
9345
9346       case '\n':
9347       case '\r':
9348       case '\t':
9349       case ' ':
9350         // ignore white-space characters
9351         break;
9352
9353       default:
9354         tape.no_valid_file = TRUE;
9355
9356         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9357
9358         break;
9359     }
9360   }
9361
9362   closeFile(file);
9363
9364   if (tape.no_valid_file)
9365     return;
9366
9367   tape.length_frames  = GetTapeLengthFrames();
9368   tape.length_seconds = GetTapeLengthSeconds();
9369 }
9370
9371 void LoadTapeFromFilename(char *filename)
9372 {
9373   char cookie[MAX_LINE_LEN];
9374   char chunk_name[CHUNK_ID_LEN + 1];
9375   File *file;
9376   int chunk_size;
9377
9378   // always start with reliable default values
9379   setTapeInfoToDefaults();
9380
9381   if (strSuffix(filename, ".sln"))
9382   {
9383     LoadTape_SokobanSolution(filename);
9384
9385     return;
9386   }
9387
9388   if (!(file = openFile(filename, MODE_READ)))
9389   {
9390     tape.no_valid_file = TRUE;
9391
9392     return;
9393   }
9394
9395   getFileChunkBE(file, chunk_name, NULL);
9396   if (strEqual(chunk_name, "RND1"))
9397   {
9398     getFile32BitBE(file);               // not used
9399
9400     getFileChunkBE(file, chunk_name, NULL);
9401     if (!strEqual(chunk_name, "TAPE"))
9402     {
9403       tape.no_valid_file = TRUE;
9404
9405       Warn("unknown format of tape file '%s'", filename);
9406
9407       closeFile(file);
9408
9409       return;
9410     }
9411   }
9412   else  // check for pre-2.0 file format with cookie string
9413   {
9414     strcpy(cookie, chunk_name);
9415     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9416       cookie[4] = '\0';
9417     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9418       cookie[strlen(cookie) - 1] = '\0';
9419
9420     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9421     {
9422       tape.no_valid_file = TRUE;
9423
9424       Warn("unknown format of tape file '%s'", filename);
9425
9426       closeFile(file);
9427
9428       return;
9429     }
9430
9431     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9432     {
9433       tape.no_valid_file = TRUE;
9434
9435       Warn("unsupported version of tape file '%s'", filename);
9436
9437       closeFile(file);
9438
9439       return;
9440     }
9441
9442     // pre-2.0 tape files have no game version, so use file version here
9443     tape.game_version = tape.file_version;
9444   }
9445
9446   if (tape.file_version < FILE_VERSION_1_2)
9447   {
9448     // tape files from versions before 1.2.0 without chunk structure
9449     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9450     LoadTape_BODY(file, 2 * tape.length,      &tape);
9451   }
9452   else
9453   {
9454     static struct
9455     {
9456       char *name;
9457       int size;
9458       int (*loader)(File *, int, struct TapeInfo *);
9459     }
9460     chunk_info[] =
9461     {
9462       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9463       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9464       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9465       { "INFO", -1,                     LoadTape_INFO },
9466       { "BODY", -1,                     LoadTape_BODY },
9467       {  NULL,  0,                      NULL }
9468     };
9469
9470     while (getFileChunkBE(file, chunk_name, &chunk_size))
9471     {
9472       int i = 0;
9473
9474       while (chunk_info[i].name != NULL &&
9475              !strEqual(chunk_name, chunk_info[i].name))
9476         i++;
9477
9478       if (chunk_info[i].name == NULL)
9479       {
9480         Warn("unknown chunk '%s' in tape file '%s'",
9481               chunk_name, filename);
9482
9483         ReadUnusedBytesFromFile(file, chunk_size);
9484       }
9485       else if (chunk_info[i].size != -1 &&
9486                chunk_info[i].size != chunk_size)
9487       {
9488         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9489               chunk_size, chunk_name, filename);
9490
9491         ReadUnusedBytesFromFile(file, chunk_size);
9492       }
9493       else
9494       {
9495         // call function to load this tape chunk
9496         int chunk_size_expected =
9497           (chunk_info[i].loader)(file, chunk_size, &tape);
9498
9499         // the size of some chunks cannot be checked before reading other
9500         // chunks first (like "HEAD" and "BODY") that contain some header
9501         // information, so check them here
9502         if (chunk_size_expected != chunk_size)
9503         {
9504           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9505                 chunk_size, chunk_name, filename);
9506         }
9507       }
9508     }
9509   }
9510
9511   closeFile(file);
9512
9513   tape.length_frames  = GetTapeLengthFrames();
9514   tape.length_seconds = GetTapeLengthSeconds();
9515
9516 #if 0
9517   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9518         tape.file_version);
9519   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9520         tape.game_version);
9521   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9522         tape.engine_version);
9523 #endif
9524 }
9525
9526 void LoadTape(int nr)
9527 {
9528   char *filename = getTapeFilename(nr);
9529
9530   LoadTapeFromFilename(filename);
9531 }
9532
9533 void LoadSolutionTape(int nr)
9534 {
9535   char *filename = getSolutionTapeFilename(nr);
9536
9537   LoadTapeFromFilename(filename);
9538
9539   if (TAPE_IS_EMPTY(tape))
9540   {
9541     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9542         level.native_bd_level->replay != NULL)
9543       CopyNativeTape_BD_to_RND(&level);
9544     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9545         level.native_sp_level->demo.is_available)
9546       CopyNativeTape_SP_to_RND(&level);
9547   }
9548 }
9549
9550 void LoadScoreTape(char *score_tape_basename, int nr)
9551 {
9552   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9553
9554   LoadTapeFromFilename(filename);
9555 }
9556
9557 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9558 {
9559   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9560
9561   LoadTapeFromFilename(filename);
9562 }
9563
9564 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9565 {
9566   // chunk required for team mode tapes with non-default screen size
9567   return (tape->num_participating_players > 1 &&
9568           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9569            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9570 }
9571
9572 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9573 {
9574   putFileVersion(file, tape->file_version);
9575   putFileVersion(file, tape->game_version);
9576 }
9577
9578 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9579 {
9580   int i;
9581   byte store_participating_players = 0;
9582
9583   // set bits for participating players for compact storage
9584   for (i = 0; i < MAX_PLAYERS; i++)
9585     if (tape->player_participates[i])
9586       store_participating_players |= (1 << i);
9587
9588   putFile32BitBE(file, tape->random_seed);
9589   putFile32BitBE(file, tape->date);
9590   putFile32BitBE(file, tape->length);
9591
9592   putFile8Bit(file, store_participating_players);
9593
9594   putFile8Bit(file, getTapeActionValue(tape));
9595
9596   putFile8Bit(file, tape->property_bits);
9597   putFile8Bit(file, tape->solved);
9598
9599   putFileVersion(file, tape->engine_version);
9600 }
9601
9602 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9603 {
9604   putFile8Bit(file, tape->scr_fieldx);
9605   putFile8Bit(file, tape->scr_fieldy);
9606 }
9607
9608 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9609 {
9610   int level_identifier_size = strlen(tape->level_identifier) + 1;
9611   int i;
9612
9613   putFile16BitBE(file, level_identifier_size);
9614
9615   for (i = 0; i < level_identifier_size; i++)
9616     putFile8Bit(file, tape->level_identifier[i]);
9617
9618   putFile16BitBE(file, tape->level_nr);
9619 }
9620
9621 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9622 {
9623   int i, j;
9624
9625   for (i = 0; i < tape->length; i++)
9626   {
9627     if (tape->use_key_actions)
9628     {
9629       for (j = 0; j < MAX_PLAYERS; j++)
9630         if (tape->player_participates[j])
9631           putFile8Bit(file, tape->pos[i].action[j]);
9632     }
9633
9634     if (tape->use_mouse_actions)
9635     {
9636       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9637       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9638       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9639     }
9640
9641     putFile8Bit(file, tape->pos[i].delay);
9642   }
9643 }
9644
9645 void SaveTapeToFilename(char *filename)
9646 {
9647   FILE *file;
9648   int tape_pos_size;
9649   int info_chunk_size;
9650   int body_chunk_size;
9651
9652   if (!(file = fopen(filename, MODE_WRITE)))
9653   {
9654     Warn("cannot save level recording file '%s'", filename);
9655
9656     return;
9657   }
9658
9659   tape_pos_size = getTapePosSize(&tape);
9660
9661   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9662   body_chunk_size = tape_pos_size * tape.length;
9663
9664   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9665   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9666
9667   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9668   SaveTape_VERS(file, &tape);
9669
9670   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9671   SaveTape_HEAD(file, &tape);
9672
9673   if (checkSaveTape_SCRN(&tape))
9674   {
9675     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9676     SaveTape_SCRN(file, &tape);
9677   }
9678
9679   putFileChunkBE(file, "INFO", info_chunk_size);
9680   SaveTape_INFO(file, &tape);
9681
9682   putFileChunkBE(file, "BODY", body_chunk_size);
9683   SaveTape_BODY(file, &tape);
9684
9685   fclose(file);
9686
9687   SetFilePermissions(filename, PERMS_PRIVATE);
9688 }
9689
9690 static void SaveTapeExt(char *filename)
9691 {
9692   int i;
9693
9694   tape.file_version = FILE_VERSION_ACTUAL;
9695   tape.game_version = GAME_VERSION_ACTUAL;
9696
9697   tape.num_participating_players = 0;
9698
9699   // count number of participating players
9700   for (i = 0; i < MAX_PLAYERS; i++)
9701     if (tape.player_participates[i])
9702       tape.num_participating_players++;
9703
9704   SaveTapeToFilename(filename);
9705
9706   tape.changed = FALSE;
9707 }
9708
9709 void SaveTape(int nr)
9710 {
9711   char *filename = getTapeFilename(nr);
9712
9713   InitTapeDirectory(leveldir_current->subdir);
9714
9715   SaveTapeExt(filename);
9716 }
9717
9718 void SaveScoreTape(int nr)
9719 {
9720   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9721
9722   // used instead of "leveldir_current->subdir" (for network games)
9723   InitScoreTapeDirectory(levelset.identifier, nr);
9724
9725   SaveTapeExt(filename);
9726 }
9727
9728 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9729                                   unsigned int req_state_added)
9730 {
9731   char *filename = getTapeFilename(nr);
9732   boolean new_tape = !fileExists(filename);
9733   boolean tape_saved = FALSE;
9734
9735   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9736   {
9737     SaveTape(nr);
9738
9739     if (new_tape)
9740       Request(msg_saved, REQ_CONFIRM | req_state_added);
9741
9742     tape_saved = TRUE;
9743   }
9744
9745   return tape_saved;
9746 }
9747
9748 boolean SaveTapeChecked(int nr)
9749 {
9750   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9751 }
9752
9753 boolean SaveTapeChecked_LevelSolved(int nr)
9754 {
9755   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9756                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9757 }
9758
9759 void DumpTape(struct TapeInfo *tape)
9760 {
9761   int tape_frame_counter;
9762   int i, j;
9763
9764   if (tape->no_valid_file)
9765   {
9766     Warn("cannot dump -- no valid tape file found");
9767
9768     return;
9769   }
9770
9771   PrintLine("-", 79);
9772
9773   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9774         tape->level_nr, tape->file_version, tape->game_version);
9775   Print("                  (effective engine version %08d)\n",
9776         tape->engine_version);
9777   Print("Level series identifier: '%s'\n", tape->level_identifier);
9778
9779   Print("Solution tape: %s\n",
9780         tape->solved ? "yes" :
9781         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9782
9783   Print("Special tape properties: ");
9784   if (tape->property_bits == TAPE_PROPERTY_NONE)
9785     Print("[none]");
9786   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9787     Print("[em_random_bug]");
9788   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9789     Print("[game_speed]");
9790   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9791     Print("[pause]");
9792   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9793     Print("[single_step]");
9794   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9795     Print("[snapshot]");
9796   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9797     Print("[replayed]");
9798   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9799     Print("[tas_keys]");
9800   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9801     Print("[small_graphics]");
9802   Print("\n");
9803
9804   int year2 = tape->date / 10000;
9805   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9806   int month_index_raw = (tape->date / 100) % 100;
9807   int month_index = month_index_raw % 12;       // prevent invalid index
9808   int month = month_index + 1;
9809   int day = tape->date % 100;
9810
9811   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9812
9813   PrintLine("-", 79);
9814
9815   tape_frame_counter = 0;
9816
9817   for (i = 0; i < tape->length; i++)
9818   {
9819     if (i >= MAX_TAPE_LEN)
9820       break;
9821
9822     Print("%04d: ", i);
9823
9824     for (j = 0; j < MAX_PLAYERS; j++)
9825     {
9826       if (tape->player_participates[j])
9827       {
9828         int action = tape->pos[i].action[j];
9829
9830         Print("%d:%02x ", j, action);
9831         Print("[%c%c%c%c|%c%c] - ",
9832               (action & JOY_LEFT ? '<' : ' '),
9833               (action & JOY_RIGHT ? '>' : ' '),
9834               (action & JOY_UP ? '^' : ' '),
9835               (action & JOY_DOWN ? 'v' : ' '),
9836               (action & JOY_BUTTON_1 ? '1' : ' '),
9837               (action & JOY_BUTTON_2 ? '2' : ' '));
9838       }
9839     }
9840
9841     Print("(%03d) ", tape->pos[i].delay);
9842     Print("[%05d]\n", tape_frame_counter);
9843
9844     tape_frame_counter += tape->pos[i].delay;
9845   }
9846
9847   PrintLine("-", 79);
9848 }
9849
9850 void DumpTapes(void)
9851 {
9852   static LevelDirTree *dumptape_leveldir = NULL;
9853
9854   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9855                                                 global.dumptape_leveldir);
9856
9857   if (dumptape_leveldir == NULL)
9858     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9859
9860   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9861       global.dumptape_level_nr > dumptape_leveldir->last_level)
9862     Fail("no such level number: %d", global.dumptape_level_nr);
9863
9864   leveldir_current = dumptape_leveldir;
9865
9866   if (options.mytapes)
9867     LoadTape(global.dumptape_level_nr);
9868   else
9869     LoadSolutionTape(global.dumptape_level_nr);
9870
9871   DumpTape(&tape);
9872
9873   CloseAllAndExit(0);
9874 }
9875
9876
9877 // ============================================================================
9878 // score file functions
9879 // ============================================================================
9880
9881 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9882 {
9883   int i;
9884
9885   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9886   {
9887     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9888     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9889     scores->entry[i].score = 0;
9890     scores->entry[i].time = 0;
9891
9892     scores->entry[i].id = -1;
9893     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9894     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9895     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9896     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9897     strcpy(scores->entry[i].country_code, "??");
9898   }
9899
9900   scores->num_entries = 0;
9901   scores->last_added = -1;
9902   scores->last_added_local = -1;
9903
9904   scores->updated = FALSE;
9905   scores->uploaded = FALSE;
9906   scores->tape_downloaded = FALSE;
9907   scores->force_last_added = FALSE;
9908
9909   // The following values are intentionally not reset here:
9910   // - last_level_nr
9911   // - last_entry_nr
9912   // - next_level_nr
9913   // - continue_playing
9914   // - continue_on_return
9915 }
9916
9917 static void setScoreInfoToDefaults(void)
9918 {
9919   setScoreInfoToDefaultsExt(&scores);
9920 }
9921
9922 static void setServerScoreInfoToDefaults(void)
9923 {
9924   setScoreInfoToDefaultsExt(&server_scores);
9925 }
9926
9927 static void LoadScore_OLD(int nr)
9928 {
9929   int i;
9930   char *filename = getScoreFilename(nr);
9931   char cookie[MAX_LINE_LEN];
9932   char line[MAX_LINE_LEN];
9933   char *line_ptr;
9934   FILE *file;
9935
9936   if (!(file = fopen(filename, MODE_READ)))
9937     return;
9938
9939   // check file identifier
9940   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9941     cookie[0] = '\0';
9942   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9943     cookie[strlen(cookie) - 1] = '\0';
9944
9945   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9946   {
9947     Warn("unknown format of score file '%s'", filename);
9948
9949     fclose(file);
9950
9951     return;
9952   }
9953
9954   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9955   {
9956     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9957       Warn("fscanf() failed; %s", strerror(errno));
9958
9959     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9960       line[0] = '\0';
9961
9962     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9963       line[strlen(line) - 1] = '\0';
9964
9965     for (line_ptr = line; *line_ptr; line_ptr++)
9966     {
9967       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9968       {
9969         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9970         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9971         break;
9972       }
9973     }
9974   }
9975
9976   fclose(file);
9977 }
9978
9979 static void ConvertScore_OLD(void)
9980 {
9981   // only convert score to time for levels that rate playing time over score
9982   if (!level.rate_time_over_score)
9983     return;
9984
9985   // convert old score to playing time for score-less levels (like Supaplex)
9986   int time_final_max = 999;
9987   int i;
9988
9989   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9990   {
9991     int score = scores.entry[i].score;
9992
9993     if (score > 0 && score < time_final_max)
9994       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9995   }
9996 }
9997
9998 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9999 {
10000   scores->file_version = getFileVersion(file);
10001   scores->game_version = getFileVersion(file);
10002
10003   return chunk_size;
10004 }
10005
10006 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10007 {
10008   char *level_identifier = NULL;
10009   int level_identifier_size;
10010   int i;
10011
10012   level_identifier_size = getFile16BitBE(file);
10013
10014   level_identifier = checked_malloc(level_identifier_size);
10015
10016   for (i = 0; i < level_identifier_size; i++)
10017     level_identifier[i] = getFile8Bit(file);
10018
10019   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10020   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10021
10022   checked_free(level_identifier);
10023
10024   scores->level_nr = getFile16BitBE(file);
10025   scores->num_entries = getFile16BitBE(file);
10026
10027   chunk_size = 2 + level_identifier_size + 2 + 2;
10028
10029   return chunk_size;
10030 }
10031
10032 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10033 {
10034   int i, j;
10035
10036   for (i = 0; i < scores->num_entries; i++)
10037   {
10038     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10039       scores->entry[i].name[j] = getFile8Bit(file);
10040
10041     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10042   }
10043
10044   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10045
10046   return chunk_size;
10047 }
10048
10049 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10050 {
10051   int i;
10052
10053   for (i = 0; i < scores->num_entries; i++)
10054     scores->entry[i].score = getFile16BitBE(file);
10055
10056   chunk_size = scores->num_entries * 2;
10057
10058   return chunk_size;
10059 }
10060
10061 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10062 {
10063   int i;
10064
10065   for (i = 0; i < scores->num_entries; i++)
10066     scores->entry[i].score = getFile32BitBE(file);
10067
10068   chunk_size = scores->num_entries * 4;
10069
10070   return chunk_size;
10071 }
10072
10073 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10074 {
10075   int i;
10076
10077   for (i = 0; i < scores->num_entries; i++)
10078     scores->entry[i].time = getFile32BitBE(file);
10079
10080   chunk_size = scores->num_entries * 4;
10081
10082   return chunk_size;
10083 }
10084
10085 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10086 {
10087   int i, j;
10088
10089   for (i = 0; i < scores->num_entries; i++)
10090   {
10091     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10092       scores->entry[i].tape_basename[j] = getFile8Bit(file);
10093
10094     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10095   }
10096
10097   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10098
10099   return chunk_size;
10100 }
10101
10102 void LoadScore(int nr)
10103 {
10104   char *filename = getScoreFilename(nr);
10105   char cookie[MAX_LINE_LEN];
10106   char chunk_name[CHUNK_ID_LEN + 1];
10107   int chunk_size;
10108   boolean old_score_file_format = FALSE;
10109   File *file;
10110
10111   // always start with reliable default values
10112   setScoreInfoToDefaults();
10113
10114   if (!(file = openFile(filename, MODE_READ)))
10115     return;
10116
10117   getFileChunkBE(file, chunk_name, NULL);
10118   if (strEqual(chunk_name, "RND1"))
10119   {
10120     getFile32BitBE(file);               // not used
10121
10122     getFileChunkBE(file, chunk_name, NULL);
10123     if (!strEqual(chunk_name, "SCOR"))
10124     {
10125       Warn("unknown format of score file '%s'", filename);
10126
10127       closeFile(file);
10128
10129       return;
10130     }
10131   }
10132   else  // check for old file format with cookie string
10133   {
10134     strcpy(cookie, chunk_name);
10135     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10136       cookie[4] = '\0';
10137     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10138       cookie[strlen(cookie) - 1] = '\0';
10139
10140     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10141     {
10142       Warn("unknown format of score file '%s'", filename);
10143
10144       closeFile(file);
10145
10146       return;
10147     }
10148
10149     old_score_file_format = TRUE;
10150   }
10151
10152   if (old_score_file_format)
10153   {
10154     // score files from versions before 4.2.4.0 without chunk structure
10155     LoadScore_OLD(nr);
10156
10157     // convert score to time, if possible (mainly for Supaplex levels)
10158     ConvertScore_OLD();
10159   }
10160   else
10161   {
10162     static struct
10163     {
10164       char *name;
10165       int size;
10166       int (*loader)(File *, int, struct ScoreInfo *);
10167     }
10168     chunk_info[] =
10169     {
10170       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10171       { "INFO", -1,                     LoadScore_INFO },
10172       { "NAME", -1,                     LoadScore_NAME },
10173       { "SCOR", -1,                     LoadScore_SCOR },
10174       { "SC4R", -1,                     LoadScore_SC4R },
10175       { "TIME", -1,                     LoadScore_TIME },
10176       { "TAPE", -1,                     LoadScore_TAPE },
10177
10178       {  NULL,  0,                      NULL }
10179     };
10180
10181     while (getFileChunkBE(file, chunk_name, &chunk_size))
10182     {
10183       int i = 0;
10184
10185       while (chunk_info[i].name != NULL &&
10186              !strEqual(chunk_name, chunk_info[i].name))
10187         i++;
10188
10189       if (chunk_info[i].name == NULL)
10190       {
10191         Warn("unknown chunk '%s' in score file '%s'",
10192               chunk_name, filename);
10193
10194         ReadUnusedBytesFromFile(file, chunk_size);
10195       }
10196       else if (chunk_info[i].size != -1 &&
10197                chunk_info[i].size != chunk_size)
10198       {
10199         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10200               chunk_size, chunk_name, filename);
10201
10202         ReadUnusedBytesFromFile(file, chunk_size);
10203       }
10204       else
10205       {
10206         // call function to load this score chunk
10207         int chunk_size_expected =
10208           (chunk_info[i].loader)(file, chunk_size, &scores);
10209
10210         // the size of some chunks cannot be checked before reading other
10211         // chunks first (like "HEAD" and "BODY") that contain some header
10212         // information, so check them here
10213         if (chunk_size_expected != chunk_size)
10214         {
10215           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10216                 chunk_size, chunk_name, filename);
10217         }
10218       }
10219     }
10220   }
10221
10222   closeFile(file);
10223 }
10224
10225 #if ENABLE_HISTORIC_CHUNKS
10226 void SaveScore_OLD(int nr)
10227 {
10228   int i;
10229   char *filename = getScoreFilename(nr);
10230   FILE *file;
10231
10232   // used instead of "leveldir_current->subdir" (for network games)
10233   InitScoreDirectory(levelset.identifier);
10234
10235   if (!(file = fopen(filename, MODE_WRITE)))
10236   {
10237     Warn("cannot save score for level %d", nr);
10238
10239     return;
10240   }
10241
10242   fprintf(file, "%s\n\n", SCORE_COOKIE);
10243
10244   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10245     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10246
10247   fclose(file);
10248
10249   SetFilePermissions(filename, PERMS_PRIVATE);
10250 }
10251 #endif
10252
10253 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10254 {
10255   putFileVersion(file, scores->file_version);
10256   putFileVersion(file, scores->game_version);
10257 }
10258
10259 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10260 {
10261   int level_identifier_size = strlen(scores->level_identifier) + 1;
10262   int i;
10263
10264   putFile16BitBE(file, level_identifier_size);
10265
10266   for (i = 0; i < level_identifier_size; i++)
10267     putFile8Bit(file, scores->level_identifier[i]);
10268
10269   putFile16BitBE(file, scores->level_nr);
10270   putFile16BitBE(file, scores->num_entries);
10271 }
10272
10273 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10274 {
10275   int i, j;
10276
10277   for (i = 0; i < scores->num_entries; i++)
10278   {
10279     int name_size = strlen(scores->entry[i].name);
10280
10281     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10282       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10283   }
10284 }
10285
10286 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10287 {
10288   int i;
10289
10290   for (i = 0; i < scores->num_entries; i++)
10291     putFile16BitBE(file, scores->entry[i].score);
10292 }
10293
10294 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10295 {
10296   int i;
10297
10298   for (i = 0; i < scores->num_entries; i++)
10299     putFile32BitBE(file, scores->entry[i].score);
10300 }
10301
10302 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10303 {
10304   int i;
10305
10306   for (i = 0; i < scores->num_entries; i++)
10307     putFile32BitBE(file, scores->entry[i].time);
10308 }
10309
10310 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10311 {
10312   int i, j;
10313
10314   for (i = 0; i < scores->num_entries; i++)
10315   {
10316     int size = strlen(scores->entry[i].tape_basename);
10317
10318     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10319       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10320   }
10321 }
10322
10323 static void SaveScoreToFilename(char *filename)
10324 {
10325   FILE *file;
10326   int info_chunk_size;
10327   int name_chunk_size;
10328   int scor_chunk_size;
10329   int sc4r_chunk_size;
10330   int time_chunk_size;
10331   int tape_chunk_size;
10332   boolean has_large_score_values;
10333   int i;
10334
10335   if (!(file = fopen(filename, MODE_WRITE)))
10336   {
10337     Warn("cannot save score file '%s'", filename);
10338
10339     return;
10340   }
10341
10342   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10343   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10344   scor_chunk_size = scores.num_entries * 2;
10345   sc4r_chunk_size = scores.num_entries * 4;
10346   time_chunk_size = scores.num_entries * 4;
10347   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10348
10349   has_large_score_values = FALSE;
10350   for (i = 0; i < scores.num_entries; i++)
10351     if (scores.entry[i].score > 0xffff)
10352       has_large_score_values = TRUE;
10353
10354   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10355   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10356
10357   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10358   SaveScore_VERS(file, &scores);
10359
10360   putFileChunkBE(file, "INFO", info_chunk_size);
10361   SaveScore_INFO(file, &scores);
10362
10363   putFileChunkBE(file, "NAME", name_chunk_size);
10364   SaveScore_NAME(file, &scores);
10365
10366   if (has_large_score_values)
10367   {
10368     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10369     SaveScore_SC4R(file, &scores);
10370   }
10371   else
10372   {
10373     putFileChunkBE(file, "SCOR", scor_chunk_size);
10374     SaveScore_SCOR(file, &scores);
10375   }
10376
10377   putFileChunkBE(file, "TIME", time_chunk_size);
10378   SaveScore_TIME(file, &scores);
10379
10380   putFileChunkBE(file, "TAPE", tape_chunk_size);
10381   SaveScore_TAPE(file, &scores);
10382
10383   fclose(file);
10384
10385   SetFilePermissions(filename, PERMS_PRIVATE);
10386 }
10387
10388 void SaveScore(int nr)
10389 {
10390   char *filename = getScoreFilename(nr);
10391   int i;
10392
10393   // used instead of "leveldir_current->subdir" (for network games)
10394   InitScoreDirectory(levelset.identifier);
10395
10396   scores.file_version = FILE_VERSION_ACTUAL;
10397   scores.game_version = GAME_VERSION_ACTUAL;
10398
10399   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10400   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10401   scores.level_nr = level_nr;
10402
10403   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10404     if (scores.entry[i].score == 0 &&
10405         scores.entry[i].time == 0 &&
10406         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10407       break;
10408
10409   scores.num_entries = i;
10410
10411   if (scores.num_entries == 0)
10412     return;
10413
10414   SaveScoreToFilename(filename);
10415 }
10416
10417 static void LoadServerScoreFromCache(int nr)
10418 {
10419   struct ScoreEntry score_entry;
10420   struct
10421   {
10422     void *value;
10423     boolean is_string;
10424     int string_size;
10425   }
10426   score_mapping[] =
10427   {
10428     { &score_entry.score,               FALSE,  0                       },
10429     { &score_entry.time,                FALSE,  0                       },
10430     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10431     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10432     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10433     { &score_entry.id,                  FALSE,  0                       },
10434     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10435     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10436     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10437     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10438
10439     { NULL,                             FALSE,  0                       }
10440   };
10441   char *filename = getScoreCacheFilename(nr);
10442   SetupFileHash *score_hash = loadSetupFileHash(filename);
10443   int i, j;
10444
10445   server_scores.num_entries = 0;
10446
10447   if (score_hash == NULL)
10448     return;
10449
10450   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10451   {
10452     score_entry = server_scores.entry[i];
10453
10454     for (j = 0; score_mapping[j].value != NULL; j++)
10455     {
10456       char token[10];
10457
10458       sprintf(token, "%02d.%d", i, j);
10459
10460       char *value = getHashEntry(score_hash, token);
10461
10462       if (value == NULL)
10463         continue;
10464
10465       if (score_mapping[j].is_string)
10466       {
10467         char *score_value = (char *)score_mapping[j].value;
10468         int value_size = score_mapping[j].string_size;
10469
10470         strncpy(score_value, value, value_size);
10471         score_value[value_size] = '\0';
10472       }
10473       else
10474       {
10475         int *score_value = (int *)score_mapping[j].value;
10476
10477         *score_value = atoi(value);
10478       }
10479
10480       server_scores.num_entries = i + 1;
10481     }
10482
10483     server_scores.entry[i] = score_entry;
10484   }
10485
10486   freeSetupFileHash(score_hash);
10487 }
10488
10489 void LoadServerScore(int nr, boolean download_score)
10490 {
10491   if (!setup.use_api_server)
10492     return;
10493
10494   // always start with reliable default values
10495   setServerScoreInfoToDefaults();
10496
10497   // 1st step: load server scores from cache file (which may not exist)
10498   // (this should prevent reading it while the thread is writing to it)
10499   LoadServerScoreFromCache(nr);
10500
10501   if (download_score && runtime.use_api_server)
10502   {
10503     // 2nd step: download server scores from score server to cache file
10504     // (as thread, as it might time out if the server is not reachable)
10505     ApiGetScoreAsThread(nr);
10506   }
10507 }
10508
10509 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10510 {
10511   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10512
10513   // if score tape not uploaded, ask for uploading missing tapes later
10514   if (!setup.has_remaining_tapes)
10515     setup.ask_for_remaining_tapes = TRUE;
10516
10517   setup.provide_uploading_tapes = TRUE;
10518   setup.has_remaining_tapes = TRUE;
10519
10520   SaveSetup_ServerSetup();
10521 }
10522
10523 void SaveServerScore(int nr, boolean tape_saved)
10524 {
10525   if (!runtime.use_api_server)
10526   {
10527     PrepareScoreTapesForUpload(leveldir_current->subdir);
10528
10529     return;
10530   }
10531
10532   ApiAddScoreAsThread(nr, tape_saved, NULL);
10533 }
10534
10535 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10536                              char *score_tape_filename)
10537 {
10538   if (!runtime.use_api_server)
10539     return;
10540
10541   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10542 }
10543
10544 void LoadLocalAndServerScore(int nr, boolean download_score)
10545 {
10546   int last_added_local = scores.last_added_local;
10547   boolean force_last_added = scores.force_last_added;
10548
10549   // needed if only showing server scores
10550   setScoreInfoToDefaults();
10551
10552   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10553     LoadScore(nr);
10554
10555   // restore last added local score entry (before merging server scores)
10556   scores.last_added = scores.last_added_local = last_added_local;
10557
10558   if (setup.use_api_server &&
10559       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10560   {
10561     // load server scores from cache file and trigger update from server
10562     LoadServerScore(nr, download_score);
10563
10564     // merge local scores with scores from server
10565     MergeServerScore();
10566   }
10567
10568   if (force_last_added)
10569     scores.force_last_added = force_last_added;
10570 }
10571
10572
10573 // ============================================================================
10574 // setup file functions
10575 // ============================================================================
10576
10577 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10578
10579
10580 static struct TokenInfo global_setup_tokens[] =
10581 {
10582   {
10583     TYPE_STRING,
10584     &setup.player_name,                         "player_name"
10585   },
10586   {
10587     TYPE_SWITCH,
10588     &setup.multiple_users,                      "multiple_users"
10589   },
10590   {
10591     TYPE_SWITCH,
10592     &setup.sound,                               "sound"
10593   },
10594   {
10595     TYPE_SWITCH,
10596     &setup.sound_loops,                         "repeating_sound_loops"
10597   },
10598   {
10599     TYPE_SWITCH,
10600     &setup.sound_music,                         "background_music"
10601   },
10602   {
10603     TYPE_SWITCH,
10604     &setup.sound_simple,                        "simple_sound_effects"
10605   },
10606   {
10607     TYPE_SWITCH,
10608     &setup.toons,                               "toons"
10609   },
10610   {
10611     TYPE_SWITCH,
10612     &setup.global_animations,                   "global_animations"
10613   },
10614   {
10615     TYPE_SWITCH,
10616     &setup.scroll_delay,                        "scroll_delay"
10617   },
10618   {
10619     TYPE_SWITCH,
10620     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10621   },
10622   {
10623     TYPE_INTEGER,
10624     &setup.scroll_delay_value,                  "scroll_delay_value"
10625   },
10626   {
10627     TYPE_STRING,
10628     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10629   },
10630   {
10631     TYPE_INTEGER,
10632     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10633   },
10634   {
10635     TYPE_SWITCH,
10636     &setup.fade_screens,                        "fade_screens"
10637   },
10638   {
10639     TYPE_SWITCH,
10640     &setup.autorecord,                          "automatic_tape_recording"
10641   },
10642   {
10643     TYPE_SWITCH,
10644     &setup.autorecord_after_replay,             "autorecord_after_replay"
10645   },
10646   {
10647     TYPE_SWITCH,
10648     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10649   },
10650   {
10651     TYPE_SWITCH,
10652     &setup.show_titlescreen,                    "show_titlescreen"
10653   },
10654   {
10655     TYPE_SWITCH,
10656     &setup.quick_doors,                         "quick_doors"
10657   },
10658   {
10659     TYPE_SWITCH,
10660     &setup.team_mode,                           "team_mode"
10661   },
10662   {
10663     TYPE_SWITCH,
10664     &setup.handicap,                            "handicap"
10665   },
10666   {
10667     TYPE_SWITCH,
10668     &setup.skip_levels,                         "skip_levels"
10669   },
10670   {
10671     TYPE_SWITCH_3_STATES,
10672     &setup.allow_skipping_levels,               "allow_skipping_levels"
10673   },
10674   {
10675     TYPE_SWITCH,
10676     &setup.increment_levels,                    "increment_levels"
10677   },
10678   {
10679     TYPE_SWITCH,
10680     &setup.auto_play_next_level,                "auto_play_next_level"
10681   },
10682   {
10683     TYPE_SWITCH,
10684     &setup.count_score_after_game,              "count_score_after_game"
10685   },
10686   {
10687     TYPE_SWITCH,
10688     &setup.show_scores_after_game,              "show_scores_after_game"
10689   },
10690   {
10691     TYPE_SWITCH,
10692     &setup.time_limit,                          "time_limit"
10693   },
10694   {
10695     TYPE_SWITCH,
10696     &setup.fullscreen,                          "fullscreen"
10697   },
10698   {
10699     TYPE_INTEGER,
10700     &setup.window_scaling_percent,              "window_scaling_percent"
10701   },
10702   {
10703     TYPE_STRING,
10704     &setup.window_scaling_quality,              "window_scaling_quality"
10705   },
10706   {
10707     TYPE_STRING,
10708     &setup.screen_rendering_mode,               "screen_rendering_mode"
10709   },
10710   {
10711     TYPE_STRING,
10712     &setup.vsync_mode,                          "vsync_mode"
10713   },
10714   {
10715     TYPE_SWITCH,
10716     &setup.ask_on_escape,                       "ask_on_escape"
10717   },
10718   {
10719     TYPE_SWITCH,
10720     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10721   },
10722   {
10723     TYPE_SWITCH,
10724     &setup.ask_on_game_over,                    "ask_on_game_over"
10725   },
10726   {
10727     TYPE_SWITCH,
10728     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10729   },
10730   {
10731     TYPE_SWITCH,
10732     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10733   },
10734   {
10735     TYPE_SWITCH,
10736     &setup.quick_switch,                        "quick_player_switch"
10737   },
10738   {
10739     TYPE_SWITCH,
10740     &setup.input_on_focus,                      "input_on_focus"
10741   },
10742   {
10743     TYPE_SWITCH,
10744     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10745   },
10746   {
10747     TYPE_SWITCH,
10748     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10749   },
10750   {
10751     TYPE_SWITCH,
10752     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10753   },
10754   {
10755     TYPE_SWITCH,
10756     &setup.game_speed_extended,                 "game_speed_extended"
10757   },
10758   {
10759     TYPE_INTEGER,
10760     &setup.game_frame_delay,                    "game_frame_delay"
10761   },
10762   {
10763     TYPE_INTEGER,
10764     &setup.default_game_engine_type,            "default_game_engine_type"
10765   },
10766   {
10767     TYPE_SWITCH,
10768     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10769   },
10770   {
10771     TYPE_SWITCH,
10772     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10773   },
10774   {
10775     TYPE_SWITCH,
10776     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10777   },
10778   {
10779     TYPE_SWITCH,
10780     &setup.bd_show_invisible_outbox,            "bd_show_invisible_outbox"
10781   },
10782   {
10783     TYPE_SWITCH_3_STATES,
10784     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10785   },
10786   {
10787     TYPE_SWITCH_3_STATES,
10788     &setup.bd_pushing_graphics,                 "bd_pushing_graphics"
10789   },
10790   {
10791     TYPE_SWITCH_3_STATES,
10792     &setup.bd_up_down_graphics,                 "bd_up_down_graphics"
10793   },
10794   {
10795     TYPE_SWITCH_3_STATES,
10796     &setup.bd_skip_falling_sounds,              "bd_skip_falling_sounds"
10797   },
10798   {
10799     TYPE_INTEGER,
10800     &setup.bd_palette_c64,                      "bd_palette_c64"
10801   },
10802   {
10803     TYPE_INTEGER,
10804     &setup.bd_palette_c64dtv,                   "bd_palette_c64dtv"
10805   },
10806   {
10807     TYPE_INTEGER,
10808     &setup.bd_palette_atari,                    "bd_palette_atari"
10809   },
10810   {
10811     TYPE_INTEGER,
10812     &setup.bd_default_color_type,               "bd_default_color_type"
10813   },
10814   {
10815     TYPE_SWITCH,
10816     &setup.bd_random_colors,                    "bd_random_colors"
10817   },
10818   {
10819     TYPE_SWITCH,
10820     &setup.sp_show_border_elements,             "sp_show_border_elements"
10821   },
10822   {
10823     TYPE_SWITCH,
10824     &setup.small_game_graphics,                 "small_game_graphics"
10825   },
10826   {
10827     TYPE_SWITCH,
10828     &setup.show_load_save_buttons,              "show_load_save_buttons"
10829   },
10830   {
10831     TYPE_SWITCH,
10832     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10833   },
10834   {
10835     TYPE_STRING,
10836     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10837   },
10838   {
10839     TYPE_STRING,
10840     &setup.graphics_set,                        "graphics_set"
10841   },
10842   {
10843     TYPE_STRING,
10844     &setup.sounds_set,                          "sounds_set"
10845   },
10846   {
10847     TYPE_STRING,
10848     &setup.music_set,                           "music_set"
10849   },
10850   {
10851     TYPE_SWITCH_3_STATES,
10852     &setup.override_level_graphics,             "override_level_graphics"
10853   },
10854   {
10855     TYPE_SWITCH_3_STATES,
10856     &setup.override_level_sounds,               "override_level_sounds"
10857   },
10858   {
10859     TYPE_SWITCH_3_STATES,
10860     &setup.override_level_music,                "override_level_music"
10861   },
10862   {
10863     TYPE_INTEGER,
10864     &setup.volume_simple,                       "volume_simple"
10865   },
10866   {
10867     TYPE_INTEGER,
10868     &setup.volume_loops,                        "volume_loops"
10869   },
10870   {
10871     TYPE_INTEGER,
10872     &setup.volume_music,                        "volume_music"
10873   },
10874   {
10875     TYPE_SWITCH,
10876     &setup.audio_sample_rate_44100,             "audio_sample_rate_44100"
10877   },
10878   {
10879     TYPE_SWITCH,
10880     &setup.network_mode,                        "network_mode"
10881   },
10882   {
10883     TYPE_PLAYER,
10884     &setup.network_player_nr,                   "network_player"
10885   },
10886   {
10887     TYPE_STRING,
10888     &setup.network_server_hostname,             "network_server_hostname"
10889   },
10890   {
10891     TYPE_STRING,
10892     &setup.touch.control_type,                  "touch.control_type"
10893   },
10894   {
10895     TYPE_INTEGER,
10896     &setup.touch.move_distance,                 "touch.move_distance"
10897   },
10898   {
10899     TYPE_INTEGER,
10900     &setup.touch.drop_distance,                 "touch.drop_distance"
10901   },
10902   {
10903     TYPE_INTEGER,
10904     &setup.touch.transparency,                  "touch.transparency"
10905   },
10906   {
10907     TYPE_INTEGER,
10908     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10909   },
10910   {
10911     TYPE_INTEGER,
10912     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10913   },
10914   {
10915     TYPE_INTEGER,
10916     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10917   },
10918   {
10919     TYPE_INTEGER,
10920     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10921   },
10922   {
10923     TYPE_INTEGER,
10924     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10925   },
10926   {
10927     TYPE_INTEGER,
10928     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10929   },
10930   {
10931     TYPE_SWITCH,
10932     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10933   },
10934 };
10935
10936 static struct TokenInfo auto_setup_tokens[] =
10937 {
10938   {
10939     TYPE_INTEGER,
10940     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10941   },
10942 };
10943
10944 static struct TokenInfo server_setup_tokens[] =
10945 {
10946   {
10947     TYPE_STRING,
10948     &setup.player_uuid,                         "player_uuid"
10949   },
10950   {
10951     TYPE_INTEGER,
10952     &setup.player_version,                      "player_version"
10953   },
10954   {
10955     TYPE_SWITCH,
10956     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10957   },
10958   {
10959     TYPE_STRING,
10960     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10961   },
10962   {
10963     TYPE_STRING,
10964     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10965   },
10966   {
10967     TYPE_SWITCH,
10968     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10969   },
10970   {
10971     TYPE_SWITCH,
10972     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10973   },
10974   {
10975     TYPE_SWITCH,
10976     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10977   },
10978   {
10979     TYPE_SWITCH,
10980     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10981   },
10982   {
10983     TYPE_SWITCH,
10984     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10985   },
10986 };
10987
10988 static struct TokenInfo editor_setup_tokens[] =
10989 {
10990   {
10991     TYPE_SWITCH,
10992     &setup.editor.el_classic,                   "editor.el_classic"
10993   },
10994   {
10995     TYPE_SWITCH,
10996     &setup.editor.el_custom,                    "editor.el_custom"
10997   },
10998   {
10999     TYPE_SWITCH,
11000     &setup.editor.el_user_defined,              "editor.el_user_defined"
11001   },
11002   {
11003     TYPE_SWITCH,
11004     &setup.editor.el_dynamic,                   "editor.el_dynamic"
11005   },
11006   {
11007     TYPE_SWITCH,
11008     &setup.editor.el_headlines,                 "editor.el_headlines"
11009   },
11010   {
11011     TYPE_SWITCH,
11012     &setup.editor.show_element_token,           "editor.show_element_token"
11013   },
11014   {
11015     TYPE_SWITCH,
11016     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
11017   },
11018 };
11019
11020 static struct TokenInfo editor_cascade_setup_tokens[] =
11021 {
11022   {
11023     TYPE_SWITCH,
11024     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
11025   },
11026   {
11027     TYPE_SWITCH,
11028     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
11029   },
11030   {
11031     TYPE_SWITCH,
11032     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
11033   },
11034   {
11035     TYPE_SWITCH,
11036     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
11037   },
11038   {
11039     TYPE_SWITCH,
11040     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
11041   },
11042   {
11043     TYPE_SWITCH,
11044     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
11045   },
11046   {
11047     TYPE_SWITCH,
11048     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
11049   },
11050   {
11051     TYPE_SWITCH,
11052     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
11053   },
11054   {
11055     TYPE_SWITCH,
11056     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
11057   },
11058   {
11059     TYPE_SWITCH,
11060     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
11061   },
11062   {
11063     TYPE_SWITCH,
11064     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
11065   },
11066   {
11067     TYPE_SWITCH,
11068     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
11069   },
11070   {
11071     TYPE_SWITCH,
11072     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
11073   },
11074   {
11075     TYPE_SWITCH,
11076     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
11077   },
11078   {
11079     TYPE_SWITCH,
11080     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
11081   },
11082   {
11083     TYPE_SWITCH,
11084     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
11085   },
11086   {
11087     TYPE_SWITCH,
11088     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
11089   },
11090   {
11091     TYPE_SWITCH,
11092     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
11093   },
11094   {
11095     TYPE_SWITCH,
11096     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
11097   },
11098   {
11099     TYPE_SWITCH,
11100     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
11101   },
11102 };
11103
11104 static struct TokenInfo shortcut_setup_tokens[] =
11105 {
11106   {
11107     TYPE_KEY_X11,
11108     &setup.shortcut.save_game,                  "shortcut.save_game"
11109   },
11110   {
11111     TYPE_KEY_X11,
11112     &setup.shortcut.load_game,                  "shortcut.load_game"
11113   },
11114   {
11115     TYPE_KEY_X11,
11116     &setup.shortcut.restart_game,               "shortcut.restart_game"
11117   },
11118   {
11119     TYPE_KEY_X11,
11120     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
11121   },
11122   {
11123     TYPE_KEY_X11,
11124     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
11125   },
11126   {
11127     TYPE_KEY_X11,
11128     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
11129   },
11130   {
11131     TYPE_KEY_X11,
11132     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
11133   },
11134   {
11135     TYPE_KEY_X11,
11136     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
11137   },
11138   {
11139     TYPE_KEY_X11,
11140     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
11141   },
11142   {
11143     TYPE_KEY_X11,
11144     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
11145   },
11146   {
11147     TYPE_KEY_X11,
11148     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11149   },
11150   {
11151     TYPE_KEY_X11,
11152     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11153   },
11154   {
11155     TYPE_KEY_X11,
11156     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11157   },
11158   {
11159     TYPE_KEY_X11,
11160     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11161   },
11162   {
11163     TYPE_KEY_X11,
11164     &setup.shortcut.tape_record,                "shortcut.tape_record"
11165   },
11166   {
11167     TYPE_KEY_X11,
11168     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11169   },
11170   {
11171     TYPE_KEY_X11,
11172     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11173   },
11174   {
11175     TYPE_KEY_X11,
11176     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11177   },
11178   {
11179     TYPE_KEY_X11,
11180     &setup.shortcut.sound_music,                "shortcut.sound_music"
11181   },
11182   {
11183     TYPE_KEY_X11,
11184     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11185   },
11186   {
11187     TYPE_KEY_X11,
11188     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11189   },
11190   {
11191     TYPE_KEY_X11,
11192     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11193   },
11194   {
11195     TYPE_KEY_X11,
11196     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11197   },
11198   {
11199     TYPE_KEY_X11,
11200     &setup.shortcut.speed_fast,                 "shortcut.speed_fast"
11201   },
11202   {
11203     TYPE_KEY_X11,
11204     &setup.shortcut.speed_slow,                 "shortcut.speed_slow"
11205   },
11206 };
11207
11208 static struct SetupInputInfo setup_input;
11209 static struct TokenInfo player_setup_tokens[] =
11210 {
11211   {
11212     TYPE_BOOLEAN,
11213     &setup_input.use_joystick,                  ".use_joystick"
11214   },
11215   {
11216     TYPE_STRING,
11217     &setup_input.joy.device_name,               ".joy.device_name"
11218   },
11219   {
11220     TYPE_INTEGER,
11221     &setup_input.joy.xleft,                     ".joy.xleft"
11222   },
11223   {
11224     TYPE_INTEGER,
11225     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11226   },
11227   {
11228     TYPE_INTEGER,
11229     &setup_input.joy.xright,                    ".joy.xright"
11230   },
11231   {
11232     TYPE_INTEGER,
11233     &setup_input.joy.yupper,                    ".joy.yupper"
11234   },
11235   {
11236     TYPE_INTEGER,
11237     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11238   },
11239   {
11240     TYPE_INTEGER,
11241     &setup_input.joy.ylower,                    ".joy.ylower"
11242   },
11243   {
11244     TYPE_INTEGER,
11245     &setup_input.joy.snap,                      ".joy.snap_field"
11246   },
11247   {
11248     TYPE_INTEGER,
11249     &setup_input.joy.drop,                      ".joy.place_bomb"
11250   },
11251   {
11252     TYPE_KEY_X11,
11253     &setup_input.key.left,                      ".key.move_left"
11254   },
11255   {
11256     TYPE_KEY_X11,
11257     &setup_input.key.right,                     ".key.move_right"
11258   },
11259   {
11260     TYPE_KEY_X11,
11261     &setup_input.key.up,                        ".key.move_up"
11262   },
11263   {
11264     TYPE_KEY_X11,
11265     &setup_input.key.down,                      ".key.move_down"
11266   },
11267   {
11268     TYPE_KEY_X11,
11269     &setup_input.key.snap,                      ".key.snap_field"
11270   },
11271   {
11272     TYPE_KEY_X11,
11273     &setup_input.key.drop,                      ".key.place_bomb"
11274   },
11275 };
11276
11277 static struct TokenInfo system_setup_tokens[] =
11278 {
11279   {
11280     TYPE_STRING,
11281     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11282   },
11283   {
11284     TYPE_STRING,
11285     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11286   },
11287   {
11288     TYPE_STRING,
11289     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11290   },
11291   {
11292     TYPE_INTEGER,
11293     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11294   },
11295 };
11296
11297 static struct TokenInfo internal_setup_tokens[] =
11298 {
11299   {
11300     TYPE_STRING,
11301     &setup.internal.program_title,              "program_title"
11302   },
11303   {
11304     TYPE_STRING,
11305     &setup.internal.program_version,            "program_version"
11306   },
11307   {
11308     TYPE_STRING,
11309     &setup.internal.program_author,             "program_author"
11310   },
11311   {
11312     TYPE_STRING,
11313     &setup.internal.program_email,              "program_email"
11314   },
11315   {
11316     TYPE_STRING,
11317     &setup.internal.program_website,            "program_website"
11318   },
11319   {
11320     TYPE_STRING,
11321     &setup.internal.program_copyright,          "program_copyright"
11322   },
11323   {
11324     TYPE_STRING,
11325     &setup.internal.program_company,            "program_company"
11326   },
11327   {
11328     TYPE_STRING,
11329     &setup.internal.program_icon_file,          "program_icon_file"
11330   },
11331   {
11332     TYPE_STRING,
11333     &setup.internal.default_graphics_set,       "default_graphics_set"
11334   },
11335   {
11336     TYPE_STRING,
11337     &setup.internal.default_sounds_set,         "default_sounds_set"
11338   },
11339   {
11340     TYPE_STRING,
11341     &setup.internal.default_music_set,          "default_music_set"
11342   },
11343   {
11344     TYPE_STRING,
11345     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11346   },
11347   {
11348     TYPE_STRING,
11349     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11350   },
11351   {
11352     TYPE_STRING,
11353     &setup.internal.fallback_music_file,        "fallback_music_file"
11354   },
11355   {
11356     TYPE_STRING,
11357     &setup.internal.default_level_series,       "default_level_series"
11358   },
11359   {
11360     TYPE_INTEGER,
11361     &setup.internal.default_window_width,       "default_window_width"
11362   },
11363   {
11364     TYPE_INTEGER,
11365     &setup.internal.default_window_height,      "default_window_height"
11366   },
11367   {
11368     TYPE_BOOLEAN,
11369     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11370   },
11371   {
11372     TYPE_BOOLEAN,
11373     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11374   },
11375   {
11376     TYPE_BOOLEAN,
11377     &setup.internal.create_user_levelset,       "create_user_levelset"
11378   },
11379   {
11380     TYPE_BOOLEAN,
11381     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11382   },
11383   {
11384     TYPE_BOOLEAN,
11385     &setup.internal.menu_game,                  "menu_game"
11386   },
11387   {
11388     TYPE_BOOLEAN,
11389     &setup.internal.menu_engines,               "menu_engines"
11390   },
11391   {
11392     TYPE_BOOLEAN,
11393     &setup.internal.menu_editor,                "menu_editor"
11394   },
11395   {
11396     TYPE_BOOLEAN,
11397     &setup.internal.menu_graphics,              "menu_graphics"
11398   },
11399   {
11400     TYPE_BOOLEAN,
11401     &setup.internal.menu_sound,                 "menu_sound"
11402   },
11403   {
11404     TYPE_BOOLEAN,
11405     &setup.internal.menu_artwork,               "menu_artwork"
11406   },
11407   {
11408     TYPE_BOOLEAN,
11409     &setup.internal.menu_input,                 "menu_input"
11410   },
11411   {
11412     TYPE_BOOLEAN,
11413     &setup.internal.menu_touch,                 "menu_touch"
11414   },
11415   {
11416     TYPE_BOOLEAN,
11417     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11418   },
11419   {
11420     TYPE_BOOLEAN,
11421     &setup.internal.menu_exit,                  "menu_exit"
11422   },
11423   {
11424     TYPE_BOOLEAN,
11425     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11426   },
11427   {
11428     TYPE_BOOLEAN,
11429     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11430   },
11431   {
11432     TYPE_BOOLEAN,
11433     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11434   },
11435   {
11436     TYPE_BOOLEAN,
11437     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11438   },
11439   {
11440     TYPE_BOOLEAN,
11441     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11442   },
11443   {
11444     TYPE_BOOLEAN,
11445     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11446   },
11447   {
11448     TYPE_BOOLEAN,
11449     &setup.internal.menu_shortcuts_speed,       "menu_shortcuts_speed"
11450   },
11451   {
11452     TYPE_BOOLEAN,
11453     &setup.internal.info_title,                 "info_title"
11454   },
11455   {
11456     TYPE_BOOLEAN,
11457     &setup.internal.info_elements,              "info_elements"
11458   },
11459   {
11460     TYPE_BOOLEAN,
11461     &setup.internal.info_music,                 "info_music"
11462   },
11463   {
11464     TYPE_BOOLEAN,
11465     &setup.internal.info_credits,               "info_credits"
11466   },
11467   {
11468     TYPE_BOOLEAN,
11469     &setup.internal.info_program,               "info_program"
11470   },
11471   {
11472     TYPE_BOOLEAN,
11473     &setup.internal.info_version,               "info_version"
11474   },
11475   {
11476     TYPE_BOOLEAN,
11477     &setup.internal.info_levelset,              "info_levelset"
11478   },
11479   {
11480     TYPE_BOOLEAN,
11481     &setup.internal.info_exit,                  "info_exit"
11482   },
11483 };
11484
11485 static struct TokenInfo debug_setup_tokens[] =
11486 {
11487   {
11488     TYPE_INTEGER,
11489     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11490   },
11491   {
11492     TYPE_INTEGER,
11493     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11494   },
11495   {
11496     TYPE_INTEGER,
11497     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11498   },
11499   {
11500     TYPE_INTEGER,
11501     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11502   },
11503   {
11504     TYPE_INTEGER,
11505     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11506   },
11507   {
11508     TYPE_INTEGER,
11509     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11510   },
11511   {
11512     TYPE_INTEGER,
11513     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11514   },
11515   {
11516     TYPE_INTEGER,
11517     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11518   },
11519   {
11520     TYPE_INTEGER,
11521     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11522   },
11523   {
11524     TYPE_INTEGER,
11525     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11526   },
11527   {
11528     TYPE_KEY_X11,
11529     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11530   },
11531   {
11532     TYPE_KEY_X11,
11533     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11534   },
11535   {
11536     TYPE_KEY_X11,
11537     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11538   },
11539   {
11540     TYPE_KEY_X11,
11541     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11542   },
11543   {
11544     TYPE_KEY_X11,
11545     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11546   },
11547   {
11548     TYPE_KEY_X11,
11549     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11550   },
11551   {
11552     TYPE_KEY_X11,
11553     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11554   },
11555   {
11556     TYPE_KEY_X11,
11557     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11558   },
11559   {
11560     TYPE_KEY_X11,
11561     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11562   },
11563   {
11564     TYPE_KEY_X11,
11565     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11566   },
11567   {
11568     TYPE_BOOLEAN,
11569     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11570   {
11571     TYPE_BOOLEAN,
11572     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11573   },
11574   {
11575     TYPE_BOOLEAN,
11576     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11577   },
11578   {
11579     TYPE_SWITCH_3_STATES,
11580     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11581   },
11582   {
11583     TYPE_INTEGER,
11584     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11585   },
11586 };
11587
11588 static struct TokenInfo options_setup_tokens[] =
11589 {
11590   {
11591     TYPE_BOOLEAN,
11592     &setup.options.verbose,                     "options.verbose"
11593   },
11594   {
11595     TYPE_BOOLEAN,
11596     &setup.options.debug,                       "options.debug"
11597   },
11598   {
11599     TYPE_STRING,
11600     &setup.options.debug_mode,                  "options.debug_mode"
11601   },
11602 };
11603
11604 static void setSetupInfoToDefaults(struct SetupInfo *si)
11605 {
11606   int i;
11607
11608   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11609
11610   si->multiple_users = TRUE;
11611
11612   si->sound = TRUE;
11613   si->sound_loops = TRUE;
11614   si->sound_music = TRUE;
11615   si->sound_simple = TRUE;
11616   si->toons = TRUE;
11617   si->global_animations = TRUE;
11618   si->scroll_delay = TRUE;
11619   si->forced_scroll_delay = FALSE;
11620   si->scroll_delay_value = STD_SCROLL_DELAY;
11621   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11622   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11623   si->fade_screens = TRUE;
11624   si->autorecord = TRUE;
11625   si->autorecord_after_replay = TRUE;
11626   si->auto_pause_on_start = FALSE;
11627   si->show_titlescreen = TRUE;
11628   si->quick_doors = FALSE;
11629   si->team_mode = FALSE;
11630   si->handicap = TRUE;
11631   si->skip_levels = TRUE;
11632   si->allow_skipping_levels = STATE_ASK;
11633   si->increment_levels = TRUE;
11634   si->auto_play_next_level = TRUE;
11635   si->count_score_after_game = TRUE;
11636   si->show_scores_after_game = TRUE;
11637   si->time_limit = TRUE;
11638   si->fullscreen = FALSE;
11639   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11640   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11641   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11642   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11643   si->ask_on_escape = TRUE;
11644   si->ask_on_escape_editor = TRUE;
11645   si->ask_on_game_over = TRUE;
11646   si->ask_on_quit_game = TRUE;
11647   si->ask_on_quit_program = TRUE;
11648   si->quick_switch = FALSE;
11649   si->input_on_focus = FALSE;
11650   si->prefer_aga_graphics = TRUE;
11651   si->prefer_lowpass_sounds = FALSE;
11652   si->prefer_extra_panel_items = TRUE;
11653   si->game_speed_extended = FALSE;
11654   si->game_frame_delay = GAME_FRAME_DELAY;
11655   si->default_game_engine_type  = GAME_ENGINE_TYPE_RND;
11656   si->bd_skip_uncovering = FALSE;
11657   si->bd_skip_hatching = FALSE;
11658   si->bd_scroll_delay = TRUE;
11659   si->bd_show_invisible_outbox = FALSE;
11660   si->bd_smooth_movements = STATE_AUTO;
11661   si->bd_pushing_graphics = STATE_TRUE;
11662   si->bd_up_down_graphics = STATE_TRUE;
11663   si->bd_skip_falling_sounds = STATE_AUTO;
11664   si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11665   si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11666   si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11667   si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11668   si->bd_random_colors = FALSE;
11669   si->sp_show_border_elements = FALSE;
11670   si->small_game_graphics = FALSE;
11671   si->show_load_save_buttons = FALSE;
11672   si->show_undo_redo_buttons = FALSE;
11673   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11674
11675   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11676   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11677   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11678
11679   si->override_level_graphics = STATE_FALSE;
11680   si->override_level_sounds = STATE_FALSE;
11681   si->override_level_music = STATE_FALSE;
11682
11683   si->volume_simple = 100;              // percent
11684   si->volume_loops = 100;               // percent
11685   si->volume_music = 100;               // percent
11686   si->audio_sample_rate_44100 = FALSE;
11687
11688   si->network_mode = FALSE;
11689   si->network_player_nr = 0;            // first player
11690   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11691
11692   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11693   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11694   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11695   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11696   si->touch.draw_outlined = TRUE;
11697   si->touch.draw_pressed = TRUE;
11698
11699   for (i = 0; i < 2; i++)
11700   {
11701     char *default_grid_button[6][2] =
11702     {
11703       { "      ", "  ^^  " },
11704       { "      ", "  ^^  " },
11705       { "      ", "<<  >>" },
11706       { "      ", "<<  >>" },
11707       { "111222", "  vv  " },
11708       { "111222", "  vv  " }
11709     };
11710     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11711     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11712     int min_xsize = MIN(6, grid_xsize);
11713     int min_ysize = MIN(6, grid_ysize);
11714     int startx = grid_xsize - min_xsize;
11715     int starty = grid_ysize - min_ysize;
11716     int x, y;
11717
11718     // virtual buttons grid can only be set to defaults if video is initialized
11719     // (this will be repeated if virtual buttons are not loaded from setup file)
11720     if (video.initialized)
11721     {
11722       si->touch.grid_xsize[i] = grid_xsize;
11723       si->touch.grid_ysize[i] = grid_ysize;
11724     }
11725     else
11726     {
11727       si->touch.grid_xsize[i] = -1;
11728       si->touch.grid_ysize[i] = -1;
11729     }
11730
11731     for (x = 0; x < MAX_GRID_XSIZE; x++)
11732       for (y = 0; y < MAX_GRID_YSIZE; y++)
11733         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11734
11735     for (x = 0; x < min_xsize; x++)
11736       for (y = 0; y < min_ysize; y++)
11737         si->touch.grid_button[i][x][starty + y] =
11738           default_grid_button[y][0][x];
11739
11740     for (x = 0; x < min_xsize; x++)
11741       for (y = 0; y < min_ysize; y++)
11742         si->touch.grid_button[i][startx + x][starty + y] =
11743           default_grid_button[y][1][x];
11744   }
11745
11746   si->touch.grid_initialized            = video.initialized;
11747
11748   si->touch.overlay_buttons             = FALSE;
11749
11750   si->editor.el_boulderdash             = TRUE;
11751   si->editor.el_boulderdash_native      = TRUE;
11752   si->editor.el_boulderdash_effects     = TRUE;
11753   si->editor.el_emerald_mine            = TRUE;
11754   si->editor.el_emerald_mine_club       = TRUE;
11755   si->editor.el_more                    = TRUE;
11756   si->editor.el_sokoban                 = TRUE;
11757   si->editor.el_supaplex                = TRUE;
11758   si->editor.el_diamond_caves           = TRUE;
11759   si->editor.el_dx_boulderdash          = TRUE;
11760
11761   si->editor.el_mirror_magic            = TRUE;
11762   si->editor.el_deflektor               = TRUE;
11763
11764   si->editor.el_chars                   = TRUE;
11765   si->editor.el_steel_chars             = TRUE;
11766
11767   si->editor.el_classic                 = TRUE;
11768   si->editor.el_custom                  = TRUE;
11769
11770   si->editor.el_user_defined            = FALSE;
11771   si->editor.el_dynamic                 = TRUE;
11772
11773   si->editor.el_headlines               = TRUE;
11774
11775   si->editor.show_element_token         = FALSE;
11776
11777   si->editor.show_read_only_warning     = TRUE;
11778
11779   si->editor.use_template_for_new_levels = TRUE;
11780
11781   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11782   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11783   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11784   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11785   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11786
11787   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11788   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11789   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11790   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11791   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11792
11793   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11794   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11795   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11796   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11797   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11798   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11799
11800   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11801   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11802   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11803
11804   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11805   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11806   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11807   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11808
11809   si->shortcut.speed_fast       = DEFAULT_KEY_SPEED_FAST;
11810   si->shortcut.speed_slow       = DEFAULT_KEY_SPEED_SLOW;
11811
11812   for (i = 0; i < MAX_PLAYERS; i++)
11813   {
11814     si->input[i].use_joystick = FALSE;
11815     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11816     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11817     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11818     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11819     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11820     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11821     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11822     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11823     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11824     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11825     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11826     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11827     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11828     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11829     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11830   }
11831
11832   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11833   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11834   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11835   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11836
11837   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11838   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11839   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11840   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11841   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11842   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11843   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11844
11845   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11846
11847   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11848   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11849   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11850
11851   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11852   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11853   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11854
11855   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11856   si->internal.choose_from_top_leveldir = FALSE;
11857   si->internal.show_scaling_in_title = TRUE;
11858   si->internal.create_user_levelset = TRUE;
11859   si->internal.info_screens_from_main = FALSE;
11860
11861   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11862   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11863
11864   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11865   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11866   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11867   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11868   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11869   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11870   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11871   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11872   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11873   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11874
11875   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11876   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11877   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11878   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11879   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11880   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11881   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11882   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11883   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11884   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11885
11886   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11887   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11888
11889   si->debug.show_frames_per_second = FALSE;
11890
11891   si->debug.xsn_mode = STATE_AUTO;
11892   si->debug.xsn_percent = 0;
11893
11894   si->options.verbose = FALSE;
11895   si->options.debug = FALSE;
11896   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11897
11898 #if defined(PLATFORM_ANDROID)
11899   si->fullscreen = TRUE;
11900   si->touch.overlay_buttons = TRUE;
11901 #endif
11902
11903   setHideSetupEntry(&setup.debug.xsn_mode);
11904 }
11905
11906 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11907 {
11908   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11909 }
11910
11911 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11912 {
11913   si->player_uuid = NULL;       // (will be set later)
11914   si->player_version = 1;       // (will be set later)
11915
11916   si->use_api_server = TRUE;
11917   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11918   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11919   si->ask_for_uploading_tapes = TRUE;
11920   si->ask_for_remaining_tapes = FALSE;
11921   si->provide_uploading_tapes = TRUE;
11922   si->ask_for_using_api_server = TRUE;
11923   si->has_remaining_tapes = FALSE;
11924 }
11925
11926 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11927 {
11928   si->editor_cascade.el_bd              = TRUE;
11929   si->editor_cascade.el_bd_native       = TRUE;
11930   si->editor_cascade.el_bd_effects      = FALSE;
11931   si->editor_cascade.el_em              = TRUE;
11932   si->editor_cascade.el_emc             = TRUE;
11933   si->editor_cascade.el_rnd             = TRUE;
11934   si->editor_cascade.el_sb              = TRUE;
11935   si->editor_cascade.el_sp              = TRUE;
11936   si->editor_cascade.el_dc              = TRUE;
11937   si->editor_cascade.el_dx              = TRUE;
11938
11939   si->editor_cascade.el_mm              = TRUE;
11940   si->editor_cascade.el_df              = TRUE;
11941
11942   si->editor_cascade.el_chars           = FALSE;
11943   si->editor_cascade.el_steel_chars     = FALSE;
11944   si->editor_cascade.el_ce              = FALSE;
11945   si->editor_cascade.el_ge              = FALSE;
11946   si->editor_cascade.el_es              = FALSE;
11947   si->editor_cascade.el_ref             = FALSE;
11948   si->editor_cascade.el_user            = FALSE;
11949   si->editor_cascade.el_dynamic         = FALSE;
11950 }
11951
11952 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11953
11954 static char *getHideSetupToken(void *setup_value)
11955 {
11956   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11957
11958   if (setup_value != NULL)
11959     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11960
11961   return hide_setup_token;
11962 }
11963
11964 void setHideSetupEntry(void *setup_value)
11965 {
11966   char *hide_setup_token = getHideSetupToken(setup_value);
11967
11968   if (hide_setup_hash == NULL)
11969     hide_setup_hash = newSetupFileHash();
11970
11971   if (setup_value != NULL)
11972     setHashEntry(hide_setup_hash, hide_setup_token, "");
11973 }
11974
11975 void removeHideSetupEntry(void *setup_value)
11976 {
11977   char *hide_setup_token = getHideSetupToken(setup_value);
11978
11979   if (setup_value != NULL)
11980     removeHashEntry(hide_setup_hash, hide_setup_token);
11981 }
11982
11983 boolean hideSetupEntry(void *setup_value)
11984 {
11985   char *hide_setup_token = getHideSetupToken(setup_value);
11986
11987   return (setup_value != NULL &&
11988           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11989 }
11990
11991 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11992                                       struct TokenInfo *token_info,
11993                                       int token_nr, char *token_text)
11994 {
11995   char *token_hide_text = getStringCat2(token_text, ".hide");
11996   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11997
11998   // set the value of this setup option in the setup option structure
11999   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12000
12001   // check if this setup option should be hidden in the setup menu
12002   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12003     setHideSetupEntry(token_info[token_nr].value);
12004
12005   free(token_hide_text);
12006 }
12007
12008 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12009                                       struct TokenInfo *token_info,
12010                                       int token_nr)
12011 {
12012   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12013                             token_info[token_nr].text);
12014 }
12015
12016 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12017 {
12018   int i, pnr;
12019
12020   if (!setup_file_hash)
12021     return;
12022
12023   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12024     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12025
12026   setup.touch.grid_initialized = TRUE;
12027   for (i = 0; i < 2; i++)
12028   {
12029     int grid_xsize = setup.touch.grid_xsize[i];
12030     int grid_ysize = setup.touch.grid_ysize[i];
12031     int x, y;
12032
12033     // if virtual buttons are not loaded from setup file, repeat initializing
12034     // virtual buttons grid with default values later when video is initialized
12035     if (grid_xsize == -1 ||
12036         grid_ysize == -1)
12037     {
12038       setup.touch.grid_initialized = FALSE;
12039
12040       continue;
12041     }
12042
12043     for (y = 0; y < grid_ysize; y++)
12044     {
12045       char token_string[MAX_LINE_LEN];
12046
12047       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12048
12049       char *value_string = getHashEntry(setup_file_hash, token_string);
12050
12051       if (value_string == NULL)
12052         continue;
12053
12054       for (x = 0; x < grid_xsize; x++)
12055       {
12056         char c = value_string[x];
12057
12058         setup.touch.grid_button[i][x][y] =
12059           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12060       }
12061     }
12062   }
12063
12064   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12065     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12066
12067   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12068     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12069
12070   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12071   {
12072     char prefix[30];
12073
12074     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12075
12076     setup_input = setup.input[pnr];
12077     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12078     {
12079       char full_token[100];
12080
12081       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12082       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12083                                 full_token);
12084     }
12085     setup.input[pnr] = setup_input;
12086   }
12087
12088   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12089     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12090
12091   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12092     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12093
12094   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12095     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12096
12097   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12098     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12099
12100   setHideRelatedSetupEntries();
12101 }
12102
12103 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12104 {
12105   int i;
12106
12107   if (!setup_file_hash)
12108     return;
12109
12110   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12111     setSetupInfo(auto_setup_tokens, i,
12112                  getHashEntry(setup_file_hash,
12113                               auto_setup_tokens[i].text));
12114 }
12115
12116 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12117 {
12118   int i;
12119
12120   if (!setup_file_hash)
12121     return;
12122
12123   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12124     setSetupInfo(server_setup_tokens, i,
12125                  getHashEntry(setup_file_hash,
12126                               server_setup_tokens[i].text));
12127 }
12128
12129 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12130 {
12131   int i;
12132
12133   if (!setup_file_hash)
12134     return;
12135
12136   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12137     setSetupInfo(editor_cascade_setup_tokens, i,
12138                  getHashEntry(setup_file_hash,
12139                               editor_cascade_setup_tokens[i].text));
12140 }
12141
12142 void LoadUserNames(void)
12143 {
12144   int last_user_nr = user.nr;
12145   int i;
12146
12147   if (global.user_names != NULL)
12148   {
12149     for (i = 0; i < MAX_PLAYER_NAMES; i++)
12150       checked_free(global.user_names[i]);
12151
12152     checked_free(global.user_names);
12153   }
12154
12155   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12156
12157   for (i = 0; i < MAX_PLAYER_NAMES; i++)
12158   {
12159     user.nr = i;
12160
12161     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12162
12163     if (setup_file_hash)
12164     {
12165       char *player_name = getHashEntry(setup_file_hash, "player_name");
12166
12167       global.user_names[i] = getFixedUserName(player_name);
12168
12169       freeSetupFileHash(setup_file_hash);
12170     }
12171
12172     if (global.user_names[i] == NULL)
12173       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12174   }
12175
12176   user.nr = last_user_nr;
12177 }
12178
12179 void LoadSetupFromFilename(char *filename)
12180 {
12181   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12182
12183   if (setup_file_hash)
12184   {
12185     decodeSetupFileHash_Default(setup_file_hash);
12186
12187     freeSetupFileHash(setup_file_hash);
12188   }
12189   else
12190   {
12191     Debug("setup", "using default setup values");
12192   }
12193 }
12194
12195 static void LoadSetup_SpecialPostProcessing(void)
12196 {
12197   char *player_name_new;
12198
12199   // needed to work around problems with fixed length strings
12200   player_name_new = getFixedUserName(setup.player_name);
12201   free(setup.player_name);
12202   setup.player_name = player_name_new;
12203
12204   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12205   if (setup.scroll_delay == FALSE)
12206   {
12207     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12208     setup.scroll_delay = TRUE;                  // now always "on"
12209   }
12210
12211   // make sure that scroll delay value stays inside valid range
12212   setup.scroll_delay_value =
12213     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12214 }
12215
12216 void LoadSetup_Default(void)
12217 {
12218   char *filename;
12219
12220   // always start with reliable default values
12221   setSetupInfoToDefaults(&setup);
12222
12223   // try to load setup values from default setup file
12224   filename = getDefaultSetupFilename();
12225
12226   if (fileExists(filename))
12227     LoadSetupFromFilename(filename);
12228
12229   // try to load setup values from platform setup file
12230   filename = getPlatformSetupFilename();
12231
12232   if (fileExists(filename))
12233     LoadSetupFromFilename(filename);
12234
12235   // try to load setup values from user setup file
12236   filename = getSetupFilename();
12237
12238   LoadSetupFromFilename(filename);
12239
12240   LoadSetup_SpecialPostProcessing();
12241 }
12242
12243 void LoadSetup_AutoSetup(void)
12244 {
12245   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12246   SetupFileHash *setup_file_hash = NULL;
12247
12248   // always start with reliable default values
12249   setSetupInfoToDefaults_AutoSetup(&setup);
12250
12251   setup_file_hash = loadSetupFileHash(filename);
12252
12253   if (setup_file_hash)
12254   {
12255     decodeSetupFileHash_AutoSetup(setup_file_hash);
12256
12257     freeSetupFileHash(setup_file_hash);
12258   }
12259
12260   free(filename);
12261 }
12262
12263 void LoadSetup_ServerSetup(void)
12264 {
12265   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12266   SetupFileHash *setup_file_hash = NULL;
12267
12268   // always start with reliable default values
12269   setSetupInfoToDefaults_ServerSetup(&setup);
12270
12271   setup_file_hash = loadSetupFileHash(filename);
12272
12273   if (setup_file_hash)
12274   {
12275     decodeSetupFileHash_ServerSetup(setup_file_hash);
12276
12277     freeSetupFileHash(setup_file_hash);
12278   }
12279
12280   free(filename);
12281
12282   if (setup.player_uuid == NULL)
12283   {
12284     // player UUID does not yet exist in setup file
12285     setup.player_uuid = getStringCopy(getUUID());
12286     setup.player_version = 2;
12287
12288     SaveSetup_ServerSetup();
12289   }
12290 }
12291
12292 void LoadSetup_EditorCascade(void)
12293 {
12294   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12295   SetupFileHash *setup_file_hash = NULL;
12296
12297   // always start with reliable default values
12298   setSetupInfoToDefaults_EditorCascade(&setup);
12299
12300   setup_file_hash = loadSetupFileHash(filename);
12301
12302   if (setup_file_hash)
12303   {
12304     decodeSetupFileHash_EditorCascade(setup_file_hash);
12305
12306     freeSetupFileHash(setup_file_hash);
12307   }
12308
12309   free(filename);
12310 }
12311
12312 void LoadSetup(void)
12313 {
12314   LoadSetup_Default();
12315   LoadSetup_AutoSetup();
12316   LoadSetup_ServerSetup();
12317   LoadSetup_EditorCascade();
12318 }
12319
12320 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12321                                            char *mapping_line)
12322 {
12323   char mapping_guid[MAX_LINE_LEN];
12324   char *mapping_start, *mapping_end;
12325
12326   // get GUID from game controller mapping line: copy complete line
12327   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12328   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12329
12330   // get GUID from game controller mapping line: cut after GUID part
12331   mapping_start = strchr(mapping_guid, ',');
12332   if (mapping_start != NULL)
12333     *mapping_start = '\0';
12334
12335   // cut newline from game controller mapping line
12336   mapping_end = strchr(mapping_line, '\n');
12337   if (mapping_end != NULL)
12338     *mapping_end = '\0';
12339
12340   // add mapping entry to game controller mappings hash
12341   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12342 }
12343
12344 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12345                                                  char *filename)
12346 {
12347   FILE *file;
12348
12349   if (!(file = fopen(filename, MODE_READ)))
12350   {
12351     Warn("cannot read game controller mappings file '%s'", filename);
12352
12353     return;
12354   }
12355
12356   while (!feof(file))
12357   {
12358     char line[MAX_LINE_LEN];
12359
12360     if (!fgets(line, MAX_LINE_LEN, file))
12361       break;
12362
12363     addGameControllerMappingToHash(mappings_hash, line);
12364   }
12365
12366   fclose(file);
12367 }
12368
12369 void SaveSetup_Default(void)
12370 {
12371   char *filename = getSetupFilename();
12372   FILE *file;
12373   int i, pnr;
12374
12375   InitUserDataDirectory();
12376
12377   if (!(file = fopen(filename, MODE_WRITE)))
12378   {
12379     Warn("cannot write setup file '%s'", filename);
12380
12381     return;
12382   }
12383
12384   fprintFileHeader(file, SETUP_FILENAME);
12385
12386   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12387   {
12388     // just to make things nicer :)
12389     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12390         global_setup_tokens[i].value == &setup.sound                    ||
12391         global_setup_tokens[i].value == &setup.graphics_set             ||
12392         global_setup_tokens[i].value == &setup.volume_simple            ||
12393         global_setup_tokens[i].value == &setup.network_mode             ||
12394         global_setup_tokens[i].value == &setup.touch.control_type       ||
12395         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12396         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12397       fprintf(file, "\n");
12398
12399     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12400   }
12401
12402   for (i = 0; i < 2; i++)
12403   {
12404     int grid_xsize = setup.touch.grid_xsize[i];
12405     int grid_ysize = setup.touch.grid_ysize[i];
12406     int x, y;
12407
12408     fprintf(file, "\n");
12409
12410     for (y = 0; y < grid_ysize; y++)
12411     {
12412       char token_string[MAX_LINE_LEN];
12413       char value_string[MAX_LINE_LEN];
12414
12415       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12416
12417       for (x = 0; x < grid_xsize; x++)
12418       {
12419         char c = setup.touch.grid_button[i][x][y];
12420
12421         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12422       }
12423
12424       value_string[grid_xsize] = '\0';
12425
12426       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12427     }
12428   }
12429
12430   fprintf(file, "\n");
12431   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12432     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12433
12434   fprintf(file, "\n");
12435   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12436     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12437
12438   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12439   {
12440     char prefix[30];
12441
12442     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12443     fprintf(file, "\n");
12444
12445     setup_input = setup.input[pnr];
12446     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12447       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12448   }
12449
12450   fprintf(file, "\n");
12451   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12452     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12453
12454   // (internal setup values not saved to user setup file)
12455
12456   fprintf(file, "\n");
12457   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12458     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12459         setup.debug.xsn_mode != STATE_AUTO)
12460       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12461
12462   fprintf(file, "\n");
12463   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12464     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12465
12466   fclose(file);
12467
12468   SetFilePermissions(filename, PERMS_PRIVATE);
12469 }
12470
12471 void SaveSetup_AutoSetup(void)
12472 {
12473   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12474   FILE *file;
12475   int i;
12476
12477   InitUserDataDirectory();
12478
12479   if (!(file = fopen(filename, MODE_WRITE)))
12480   {
12481     Warn("cannot write auto setup file '%s'", filename);
12482
12483     free(filename);
12484
12485     return;
12486   }
12487
12488   fprintFileHeader(file, AUTOSETUP_FILENAME);
12489
12490   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12491     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12492
12493   fclose(file);
12494
12495   SetFilePermissions(filename, PERMS_PRIVATE);
12496
12497   free(filename);
12498 }
12499
12500 void SaveSetup_ServerSetup(void)
12501 {
12502   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12503   FILE *file;
12504   int i;
12505
12506   InitUserDataDirectory();
12507
12508   if (!(file = fopen(filename, MODE_WRITE)))
12509   {
12510     Warn("cannot write server setup file '%s'", filename);
12511
12512     free(filename);
12513
12514     return;
12515   }
12516
12517   fprintFileHeader(file, SERVERSETUP_FILENAME);
12518
12519   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12520   {
12521     // just to make things nicer :)
12522     if (server_setup_tokens[i].value == &setup.use_api_server)
12523       fprintf(file, "\n");
12524
12525     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12526   }
12527
12528   fclose(file);
12529
12530   SetFilePermissions(filename, PERMS_PRIVATE);
12531
12532   free(filename);
12533 }
12534
12535 void SaveSetup_EditorCascade(void)
12536 {
12537   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12538   FILE *file;
12539   int i;
12540
12541   InitUserDataDirectory();
12542
12543   if (!(file = fopen(filename, MODE_WRITE)))
12544   {
12545     Warn("cannot write editor cascade state file '%s'", filename);
12546
12547     free(filename);
12548
12549     return;
12550   }
12551
12552   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12553
12554   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12555     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12556
12557   fclose(file);
12558
12559   SetFilePermissions(filename, PERMS_PRIVATE);
12560
12561   free(filename);
12562 }
12563
12564 void SaveSetup(void)
12565 {
12566   SaveSetup_Default();
12567   SaveSetup_AutoSetup();
12568   SaveSetup_ServerSetup();
12569   SaveSetup_EditorCascade();
12570 }
12571
12572 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12573                                                   char *filename)
12574 {
12575   FILE *file;
12576
12577   if (!(file = fopen(filename, MODE_WRITE)))
12578   {
12579     Warn("cannot write game controller mappings file '%s'", filename);
12580
12581     return;
12582   }
12583
12584   BEGIN_HASH_ITERATION(mappings_hash, itr)
12585   {
12586     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12587   }
12588   END_HASH_ITERATION(mappings_hash, itr)
12589
12590   fclose(file);
12591 }
12592
12593 void SaveSetup_AddGameControllerMapping(char *mapping)
12594 {
12595   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12596   SetupFileHash *mappings_hash = newSetupFileHash();
12597
12598   InitUserDataDirectory();
12599
12600   // load existing personal game controller mappings
12601   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12602
12603   // add new mapping to personal game controller mappings
12604   addGameControllerMappingToHash(mappings_hash, mapping);
12605
12606   // save updated personal game controller mappings
12607   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12608
12609   freeSetupFileHash(mappings_hash);
12610   free(filename);
12611 }
12612
12613 void LoadCustomElementDescriptions(void)
12614 {
12615   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12616   SetupFileHash *setup_file_hash;
12617   int i;
12618
12619   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12620   {
12621     if (element_info[i].custom_description != NULL)
12622     {
12623       free(element_info[i].custom_description);
12624       element_info[i].custom_description = NULL;
12625     }
12626   }
12627
12628   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12629     return;
12630
12631   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12632   {
12633     char *token = getStringCat2(element_info[i].token_name, ".name");
12634     char *value = getHashEntry(setup_file_hash, token);
12635
12636     if (value != NULL)
12637       element_info[i].custom_description = getStringCopy(value);
12638
12639     free(token);
12640   }
12641
12642   freeSetupFileHash(setup_file_hash);
12643 }
12644
12645 static int getElementFromToken(char *token)
12646 {
12647   char *value = getHashEntry(element_token_hash, token);
12648
12649   if (value != NULL)
12650     return atoi(value);
12651
12652   Warn("unknown element token '%s'", token);
12653
12654   return EL_UNDEFINED;
12655 }
12656
12657 void FreeGlobalAnimEventInfo(void)
12658 {
12659   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12660
12661   if (gaei->event_list == NULL)
12662     return;
12663
12664   int i;
12665
12666   for (i = 0; i < gaei->num_event_lists; i++)
12667   {
12668     checked_free(gaei->event_list[i]->event_value);
12669     checked_free(gaei->event_list[i]);
12670   }
12671
12672   checked_free(gaei->event_list);
12673
12674   gaei->event_list = NULL;
12675   gaei->num_event_lists = 0;
12676 }
12677
12678 static int AddGlobalAnimEventList(void)
12679 {
12680   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12681   int list_pos = gaei->num_event_lists++;
12682
12683   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12684                                      sizeof(struct GlobalAnimEventListInfo *));
12685
12686   gaei->event_list[list_pos] =
12687     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12688
12689   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12690
12691   gaeli->event_value = NULL;
12692   gaeli->num_event_values = 0;
12693
12694   return list_pos;
12695 }
12696
12697 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12698 {
12699   // do not add empty global animation events
12700   if (event_value == ANIM_EVENT_NONE)
12701     return list_pos;
12702
12703   // if list position is undefined, create new list
12704   if (list_pos == ANIM_EVENT_UNDEFINED)
12705     list_pos = AddGlobalAnimEventList();
12706
12707   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12708   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12709   int value_pos = gaeli->num_event_values++;
12710
12711   gaeli->event_value = checked_realloc(gaeli->event_value,
12712                                        gaeli->num_event_values * sizeof(int *));
12713
12714   gaeli->event_value[value_pos] = event_value;
12715
12716   return list_pos;
12717 }
12718
12719 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12720 {
12721   if (list_pos == ANIM_EVENT_UNDEFINED)
12722     return ANIM_EVENT_NONE;
12723
12724   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12725   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12726
12727   return gaeli->event_value[value_pos];
12728 }
12729
12730 int GetGlobalAnimEventValueCount(int list_pos)
12731 {
12732   if (list_pos == ANIM_EVENT_UNDEFINED)
12733     return 0;
12734
12735   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12736   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12737
12738   return gaeli->num_event_values;
12739 }
12740
12741 // This function checks if a string <s> of the format "string1, string2, ..."
12742 // exactly contains a string <s_contained>.
12743
12744 static boolean string_has_parameter(char *s, char *s_contained)
12745 {
12746   char *substring;
12747
12748   if (s == NULL || s_contained == NULL)
12749     return FALSE;
12750
12751   if (strlen(s_contained) > strlen(s))
12752     return FALSE;
12753
12754   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12755   {
12756     char next_char = s[strlen(s_contained)];
12757
12758     // check if next character is delimiter or whitespace
12759     if (next_char == ',' || next_char == '\0' ||
12760         next_char == ' ' || next_char == '\t')
12761       return TRUE;
12762   }
12763
12764   // check if string contains another parameter string after a comma
12765   substring = strchr(s, ',');
12766   if (substring == NULL)        // string does not contain a comma
12767     return FALSE;
12768
12769   // advance string pointer to next character after the comma
12770   substring++;
12771
12772   // skip potential whitespaces after the comma
12773   while (*substring == ' ' || *substring == '\t')
12774     substring++;
12775
12776   return string_has_parameter(substring, s_contained);
12777 }
12778
12779 static int get_anim_parameter_value_ce(char *s)
12780 {
12781   char *s_ptr = s;
12782   char *pattern_1 = "ce_change:custom_";
12783   char *pattern_2 = ".page_";
12784   int pattern_1_len = strlen(pattern_1);
12785   char *matching_char = strstr(s_ptr, pattern_1);
12786   int result = ANIM_EVENT_NONE;
12787
12788   if (matching_char == NULL)
12789     return ANIM_EVENT_NONE;
12790
12791   result = ANIM_EVENT_CE_CHANGE;
12792
12793   s_ptr = matching_char + pattern_1_len;
12794
12795   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12796   if (*s_ptr >= '0' && *s_ptr <= '9')
12797   {
12798     int gic_ce_nr = (*s_ptr++ - '0');
12799
12800     if (*s_ptr >= '0' && *s_ptr <= '9')
12801     {
12802       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12803
12804       if (*s_ptr >= '0' && *s_ptr <= '9')
12805         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12806     }
12807
12808     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12809       return ANIM_EVENT_NONE;
12810
12811     // custom element stored as 0 to 255
12812     gic_ce_nr--;
12813
12814     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12815   }
12816   else
12817   {
12818     // invalid custom element number specified
12819
12820     return ANIM_EVENT_NONE;
12821   }
12822
12823   // check for change page number ("page_X" or "page_XX") (optional)
12824   if (strPrefix(s_ptr, pattern_2))
12825   {
12826     s_ptr += strlen(pattern_2);
12827
12828     if (*s_ptr >= '0' && *s_ptr <= '9')
12829     {
12830       int gic_page_nr = (*s_ptr++ - '0');
12831
12832       if (*s_ptr >= '0' && *s_ptr <= '9')
12833         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12834
12835       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12836         return ANIM_EVENT_NONE;
12837
12838       // change page stored as 1 to 32 (0 means "all change pages")
12839
12840       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12841     }
12842     else
12843     {
12844       // invalid animation part number specified
12845
12846       return ANIM_EVENT_NONE;
12847     }
12848   }
12849
12850   // discard result if next character is neither delimiter nor whitespace
12851   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12852         *s_ptr == ' ' || *s_ptr == '\t'))
12853     return ANIM_EVENT_NONE;
12854
12855   return result;
12856 }
12857
12858 static int get_anim_parameter_value(char *s)
12859 {
12860   int event_value[] =
12861   {
12862     ANIM_EVENT_CLICK,
12863     ANIM_EVENT_INIT,
12864     ANIM_EVENT_START,
12865     ANIM_EVENT_END,
12866     ANIM_EVENT_POST
12867   };
12868   char *pattern_1[] =
12869   {
12870     "click:anim_",
12871     "init:anim_",
12872     "start:anim_",
12873     "end:anim_",
12874     "post:anim_"
12875   };
12876   char *pattern_2 = ".part_";
12877   char *matching_char = NULL;
12878   char *s_ptr = s;
12879   int pattern_1_len = 0;
12880   int result = ANIM_EVENT_NONE;
12881   int i;
12882
12883   result = get_anim_parameter_value_ce(s);
12884
12885   if (result != ANIM_EVENT_NONE)
12886     return result;
12887
12888   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12889   {
12890     matching_char = strstr(s_ptr, pattern_1[i]);
12891     pattern_1_len = strlen(pattern_1[i]);
12892     result = event_value[i];
12893
12894     if (matching_char != NULL)
12895       break;
12896   }
12897
12898   if (matching_char == NULL)
12899     return ANIM_EVENT_NONE;
12900
12901   s_ptr = matching_char + pattern_1_len;
12902
12903   // check for main animation number ("anim_X" or "anim_XX")
12904   if (*s_ptr >= '0' && *s_ptr <= '9')
12905   {
12906     int gic_anim_nr = (*s_ptr++ - '0');
12907
12908     if (*s_ptr >= '0' && *s_ptr <= '9')
12909       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12910
12911     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12912       return ANIM_EVENT_NONE;
12913
12914     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12915   }
12916   else
12917   {
12918     // invalid main animation number specified
12919
12920     return ANIM_EVENT_NONE;
12921   }
12922
12923   // check for animation part number ("part_X" or "part_XX") (optional)
12924   if (strPrefix(s_ptr, pattern_2))
12925   {
12926     s_ptr += strlen(pattern_2);
12927
12928     if (*s_ptr >= '0' && *s_ptr <= '9')
12929     {
12930       int gic_part_nr = (*s_ptr++ - '0');
12931
12932       if (*s_ptr >= '0' && *s_ptr <= '9')
12933         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12934
12935       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12936         return ANIM_EVENT_NONE;
12937
12938       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12939     }
12940     else
12941     {
12942       // invalid animation part number specified
12943
12944       return ANIM_EVENT_NONE;
12945     }
12946   }
12947
12948   // discard result if next character is neither delimiter nor whitespace
12949   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12950         *s_ptr == ' ' || *s_ptr == '\t'))
12951     return ANIM_EVENT_NONE;
12952
12953   return result;
12954 }
12955
12956 static int get_anim_parameter_values(char *s)
12957 {
12958   int list_pos = ANIM_EVENT_UNDEFINED;
12959   int event_value = ANIM_EVENT_DEFAULT;
12960
12961   if (string_has_parameter(s, "any"))
12962     event_value |= ANIM_EVENT_ANY;
12963
12964   if (string_has_parameter(s, "click:self") ||
12965       string_has_parameter(s, "click") ||
12966       string_has_parameter(s, "self"))
12967     event_value |= ANIM_EVENT_SELF;
12968
12969   if (string_has_parameter(s, "unclick:any"))
12970     event_value |= ANIM_EVENT_UNCLICK_ANY;
12971
12972   // if animation event found, add it to global animation event list
12973   if (event_value != ANIM_EVENT_NONE)
12974     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12975
12976   while (s != NULL)
12977   {
12978     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12979     event_value = get_anim_parameter_value(s);
12980
12981     // if animation event found, add it to global animation event list
12982     if (event_value != ANIM_EVENT_NONE)
12983       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12984
12985     // continue with next part of the string, starting with next comma
12986     s = strchr(s + 1, ',');
12987   }
12988
12989   return list_pos;
12990 }
12991
12992 static int get_anim_action_parameter_value(char *token)
12993 {
12994   // check most common default case first to massively speed things up
12995   if (strEqual(token, ARG_UNDEFINED))
12996     return ANIM_EVENT_ACTION_NONE;
12997
12998   int result = getImageIDFromToken(token);
12999
13000   if (result == -1)
13001   {
13002     char *gfx_token = getStringCat2("gfx.", token);
13003
13004     result = getImageIDFromToken(gfx_token);
13005
13006     checked_free(gfx_token);
13007   }
13008
13009   if (result == -1)
13010   {
13011     Key key = getKeyFromX11KeyName(token);
13012
13013     if (key != KSYM_UNDEFINED)
13014       result = -(int)key;
13015   }
13016
13017   if (result == -1)
13018   {
13019     if (isURL(token))
13020     {
13021       result = get_hash_from_string(token);     // unsigned int => int
13022       result = ABS(result);                     // may be negative now
13023       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13024
13025       setHashEntry(anim_url_hash, int2str(result, 0), token);
13026     }
13027   }
13028
13029   if (result == -1)
13030     result = ANIM_EVENT_ACTION_NONE;
13031
13032   return result;
13033 }
13034
13035 int get_parameter_value(char *value_raw, char *suffix, int type)
13036 {
13037   char *value = getStringToLower(value_raw);
13038   int result = 0;       // probably a save default value
13039
13040   if (strEqual(suffix, ".direction"))
13041   {
13042     result = (strEqual(value, "left")  ? MV_LEFT :
13043               strEqual(value, "right") ? MV_RIGHT :
13044               strEqual(value, "up")    ? MV_UP :
13045               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
13046   }
13047   else if (strEqual(suffix, ".position"))
13048   {
13049     result = (strEqual(value, "left")   ? POS_LEFT :
13050               strEqual(value, "right")  ? POS_RIGHT :
13051               strEqual(value, "top")    ? POS_TOP :
13052               strEqual(value, "upper")  ? POS_UPPER :
13053               strEqual(value, "middle") ? POS_MIDDLE :
13054               strEqual(value, "lower")  ? POS_LOWER :
13055               strEqual(value, "bottom") ? POS_BOTTOM :
13056               strEqual(value, "any")    ? POS_ANY :
13057               strEqual(value, "ce")     ? POS_CE :
13058               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13059               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
13060   }
13061   else if (strEqual(suffix, ".align"))
13062   {
13063     result = (strEqual(value, "left")   ? ALIGN_LEFT :
13064               strEqual(value, "right")  ? ALIGN_RIGHT :
13065               strEqual(value, "center") ? ALIGN_CENTER :
13066               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13067   }
13068   else if (strEqual(suffix, ".valign"))
13069   {
13070     result = (strEqual(value, "top")    ? VALIGN_TOP :
13071               strEqual(value, "bottom") ? VALIGN_BOTTOM :
13072               strEqual(value, "middle") ? VALIGN_MIDDLE :
13073               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13074   }
13075   else if (strEqual(suffix, ".anim_mode"))
13076   {
13077     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
13078               string_has_parameter(value, "loop")       ? ANIM_LOOP :
13079               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
13080               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
13081               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
13082               string_has_parameter(value, "random")     ? ANIM_RANDOM :
13083               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13084               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
13085               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
13086               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
13087               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13088               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
13089               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
13090               string_has_parameter(value, "all")        ? ANIM_ALL :
13091               string_has_parameter(value, "tiled")      ? ANIM_TILED :
13092               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
13093               ANIM_DEFAULT);
13094
13095     if (string_has_parameter(value, "once"))
13096       result |= ANIM_ONCE;
13097
13098     if (string_has_parameter(value, "reverse"))
13099       result |= ANIM_REVERSE;
13100
13101     if (string_has_parameter(value, "opaque_player"))
13102       result |= ANIM_OPAQUE_PLAYER;
13103
13104     if (string_has_parameter(value, "static_panel"))
13105       result |= ANIM_STATIC_PANEL;
13106   }
13107   else if (strEqual(suffix, ".init_event") ||
13108            strEqual(suffix, ".anim_event"))
13109   {
13110     result = get_anim_parameter_values(value);
13111   }
13112   else if (strEqual(suffix, ".init_delay_action") ||
13113            strEqual(suffix, ".anim_delay_action") ||
13114            strEqual(suffix, ".post_delay_action") ||
13115            strEqual(suffix, ".init_event_action") ||
13116            strEqual(suffix, ".anim_event_action"))
13117   {
13118     result = get_anim_action_parameter_value(value_raw);
13119   }
13120   else if (strEqual(suffix, ".class"))
13121   {
13122     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13123               get_hash_from_string(value));
13124   }
13125   else if (strEqual(suffix, ".style"))
13126   {
13127     result = STYLE_DEFAULT;
13128
13129     if (string_has_parameter(value, "accurate_borders"))
13130       result |= STYLE_ACCURATE_BORDERS;
13131
13132     if (string_has_parameter(value, "inner_corners"))
13133       result |= STYLE_INNER_CORNERS;
13134
13135     if (string_has_parameter(value, "reverse"))
13136       result |= STYLE_REVERSE;
13137
13138     if (string_has_parameter(value, "leftmost_position"))
13139       result |= STYLE_LEFTMOST_POSITION;
13140
13141     if (string_has_parameter(value, "block_clicks"))
13142       result |= STYLE_BLOCK;
13143
13144     if (string_has_parameter(value, "passthrough_clicks"))
13145       result |= STYLE_PASSTHROUGH;
13146
13147     if (string_has_parameter(value, "multiple_actions"))
13148       result |= STYLE_MULTIPLE_ACTIONS;
13149
13150     if (string_has_parameter(value, "consume_ce_event"))
13151       result |= STYLE_CONSUME_CE_EVENT;
13152   }
13153   else if (strEqual(suffix, ".fade_mode"))
13154   {
13155     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
13156               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
13157               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
13158               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
13159               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
13160               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
13161               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
13162               FADE_MODE_DEFAULT);
13163   }
13164   else if (strEqual(suffix, ".auto_delay_unit"))
13165   {
13166     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
13167               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13168               AUTO_DELAY_UNIT_DEFAULT);
13169   }
13170   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
13171   {
13172     result = gfx.get_font_from_token_function(value);
13173   }
13174   else          // generic parameter of type integer or boolean
13175   {
13176     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13177               type == TYPE_INTEGER ? get_integer_from_string(value) :
13178               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13179               ARG_UNDEFINED_VALUE);
13180   }
13181
13182   free(value);
13183
13184   return result;
13185 }
13186
13187 static int get_token_parameter_value(char *token, char *value_raw)
13188 {
13189   char *suffix;
13190
13191   if (token == NULL || value_raw == NULL)
13192     return ARG_UNDEFINED_VALUE;
13193
13194   suffix = strrchr(token, '.');
13195   if (suffix == NULL)
13196     suffix = token;
13197
13198   if (strEqual(suffix, ".element"))
13199     return getElementFromToken(value_raw);
13200
13201   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13202   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13203 }
13204
13205 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13206                                      boolean ignore_defaults)
13207 {
13208   int i;
13209
13210   for (i = 0; image_config_vars[i].token != NULL; i++)
13211   {
13212     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13213
13214     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13215     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13216       continue;
13217
13218     if (value != NULL)
13219       *image_config_vars[i].value =
13220         get_token_parameter_value(image_config_vars[i].token, value);
13221   }
13222 }
13223
13224 void InitMenuDesignSettings_Static(void)
13225 {
13226   // always start with reliable default values from static default config
13227   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13228 }
13229
13230 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13231 {
13232   int i;
13233
13234   // the following initializes hierarchical values from static configuration
13235
13236   // special case: initialize "ARG_DEFAULT" values in static default config
13237   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13238   titlescreen_initial_first_default.fade_mode  =
13239     title_initial_first_default.fade_mode;
13240   titlescreen_initial_first_default.fade_delay =
13241     title_initial_first_default.fade_delay;
13242   titlescreen_initial_first_default.post_delay =
13243     title_initial_first_default.post_delay;
13244   titlescreen_initial_first_default.auto_delay =
13245     title_initial_first_default.auto_delay;
13246   titlescreen_initial_first_default.auto_delay_unit =
13247     title_initial_first_default.auto_delay_unit;
13248   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13249   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13250   titlescreen_first_default.post_delay = title_first_default.post_delay;
13251   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13252   titlescreen_first_default.auto_delay_unit =
13253     title_first_default.auto_delay_unit;
13254   titlemessage_initial_first_default.fade_mode  =
13255     title_initial_first_default.fade_mode;
13256   titlemessage_initial_first_default.fade_delay =
13257     title_initial_first_default.fade_delay;
13258   titlemessage_initial_first_default.post_delay =
13259     title_initial_first_default.post_delay;
13260   titlemessage_initial_first_default.auto_delay =
13261     title_initial_first_default.auto_delay;
13262   titlemessage_initial_first_default.auto_delay_unit =
13263     title_initial_first_default.auto_delay_unit;
13264   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13265   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13266   titlemessage_first_default.post_delay = title_first_default.post_delay;
13267   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13268   titlemessage_first_default.auto_delay_unit =
13269     title_first_default.auto_delay_unit;
13270
13271   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13272   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13273   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13274   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13275   titlescreen_initial_default.auto_delay_unit =
13276     title_initial_default.auto_delay_unit;
13277   titlescreen_default.fade_mode  = title_default.fade_mode;
13278   titlescreen_default.fade_delay = title_default.fade_delay;
13279   titlescreen_default.post_delay = title_default.post_delay;
13280   titlescreen_default.auto_delay = title_default.auto_delay;
13281   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13282   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13283   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13284   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13285   titlemessage_initial_default.auto_delay_unit =
13286     title_initial_default.auto_delay_unit;
13287   titlemessage_default.fade_mode  = title_default.fade_mode;
13288   titlemessage_default.fade_delay = title_default.fade_delay;
13289   titlemessage_default.post_delay = title_default.post_delay;
13290   titlemessage_default.auto_delay = title_default.auto_delay;
13291   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13292
13293   // special case: initialize "ARG_DEFAULT" values in static default config
13294   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13295   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13296   {
13297     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13298     titlescreen_first[i] = titlescreen_first_default;
13299     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13300     titlemessage_first[i] = titlemessage_first_default;
13301
13302     titlescreen_initial[i] = titlescreen_initial_default;
13303     titlescreen[i] = titlescreen_default;
13304     titlemessage_initial[i] = titlemessage_initial_default;
13305     titlemessage[i] = titlemessage_default;
13306   }
13307
13308   // special case: initialize "ARG_DEFAULT" values in static default config
13309   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13310   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13311   {
13312     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13313       continue;
13314
13315     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13316     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13317     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13318   }
13319
13320   // special case: initialize "ARG_DEFAULT" values in static default config
13321   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13322   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13323   {
13324     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13325     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13326     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13327
13328     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13329       continue;
13330
13331     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13332   }
13333 }
13334
13335 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13336 {
13337   static struct
13338   {
13339     struct XY *dst, *src;
13340   }
13341   game_buttons_xy[] =
13342   {
13343     { &game.button.save,        &game.button.stop       },
13344     { &game.button.pause2,      &game.button.pause      },
13345     { &game.button.load,        &game.button.play       },
13346     { &game.button.undo,        &game.button.stop       },
13347     { &game.button.redo,        &game.button.play       },
13348
13349     { NULL,                     NULL                    }
13350   };
13351   int i, j;
13352
13353   // special case: initialize later added SETUP list size from LEVELS value
13354   if (menu.list_size[GAME_MODE_SETUP] == -1)
13355     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13356
13357   // set default position for snapshot buttons to stop/pause/play buttons
13358   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13359     if ((*game_buttons_xy[i].dst).x == -1 &&
13360         (*game_buttons_xy[i].dst).y == -1)
13361       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13362
13363   // --------------------------------------------------------------------------
13364   // dynamic viewports (including playfield margins, borders and alignments)
13365   // --------------------------------------------------------------------------
13366
13367   // dynamic viewports currently only supported for landscape mode
13368   int display_width  = MAX(video.display_width, video.display_height);
13369   int display_height = MIN(video.display_width, video.display_height);
13370
13371   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13372   {
13373     struct RectWithBorder *vp_window    = &viewport.window[i];
13374     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13375     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13376     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13377     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13378     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13379     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13380     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13381
13382     // adjust window size if min/max width/height is specified
13383
13384     if (vp_window->min_width != -1)
13385     {
13386       int window_width = display_width;
13387
13388       // when using static window height, use aspect ratio of display
13389       if (vp_window->min_height == -1)
13390         window_width = vp_window->height * display_width / display_height;
13391
13392       vp_window->width = MAX(vp_window->min_width, window_width);
13393     }
13394
13395     if (vp_window->min_height != -1)
13396     {
13397       int window_height = display_height;
13398
13399       // when using static window width, use aspect ratio of display
13400       if (vp_window->min_width == -1)
13401         window_height = vp_window->width * display_height / display_width;
13402
13403       vp_window->height = MAX(vp_window->min_height, window_height);
13404     }
13405
13406     if (vp_window->max_width != -1)
13407       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13408
13409     if (vp_window->max_height != -1)
13410       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13411
13412     int playfield_width  = vp_window->width;
13413     int playfield_height = vp_window->height;
13414
13415     // adjust playfield size and position according to specified margins
13416
13417     playfield_width  -= vp_playfield->margin_left;
13418     playfield_width  -= vp_playfield->margin_right;
13419
13420     playfield_height -= vp_playfield->margin_top;
13421     playfield_height -= vp_playfield->margin_bottom;
13422
13423     // adjust playfield size if min/max width/height is specified
13424
13425     if (vp_playfield->min_width != -1)
13426       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13427
13428     if (vp_playfield->min_height != -1)
13429       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13430
13431     if (vp_playfield->max_width != -1)
13432       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13433
13434     if (vp_playfield->max_height != -1)
13435       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13436
13437     // adjust playfield position according to specified alignment
13438
13439     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13440       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13441     else if (vp_playfield->align == ALIGN_CENTER)
13442       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13443     else if (vp_playfield->align == ALIGN_RIGHT)
13444       vp_playfield->x += playfield_width - vp_playfield->width;
13445
13446     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13447       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13448     else if (vp_playfield->valign == VALIGN_MIDDLE)
13449       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13450     else if (vp_playfield->valign == VALIGN_BOTTOM)
13451       vp_playfield->y += playfield_height - vp_playfield->height;
13452
13453     vp_playfield->x += vp_playfield->margin_left;
13454     vp_playfield->y += vp_playfield->margin_top;
13455
13456     // adjust individual playfield borders if only default border is specified
13457
13458     if (vp_playfield->border_left == -1)
13459       vp_playfield->border_left = vp_playfield->border_size;
13460     if (vp_playfield->border_right == -1)
13461       vp_playfield->border_right = vp_playfield->border_size;
13462     if (vp_playfield->border_top == -1)
13463       vp_playfield->border_top = vp_playfield->border_size;
13464     if (vp_playfield->border_bottom == -1)
13465       vp_playfield->border_bottom = vp_playfield->border_size;
13466
13467     // set dynamic playfield borders if borders are specified as undefined
13468     // (but only if window size was dynamic and playfield size was static)
13469
13470     if (dynamic_window_width && !dynamic_playfield_width)
13471     {
13472       if (vp_playfield->border_left == -1)
13473       {
13474         vp_playfield->border_left = (vp_playfield->x -
13475                                      vp_playfield->margin_left);
13476         vp_playfield->x     -= vp_playfield->border_left;
13477         vp_playfield->width += vp_playfield->border_left;
13478       }
13479
13480       if (vp_playfield->border_right == -1)
13481       {
13482         vp_playfield->border_right = (vp_window->width -
13483                                       vp_playfield->x -
13484                                       vp_playfield->width -
13485                                       vp_playfield->margin_right);
13486         vp_playfield->width += vp_playfield->border_right;
13487       }
13488     }
13489
13490     if (dynamic_window_height && !dynamic_playfield_height)
13491     {
13492       if (vp_playfield->border_top == -1)
13493       {
13494         vp_playfield->border_top = (vp_playfield->y -
13495                                     vp_playfield->margin_top);
13496         vp_playfield->y      -= vp_playfield->border_top;
13497         vp_playfield->height += vp_playfield->border_top;
13498       }
13499
13500       if (vp_playfield->border_bottom == -1)
13501       {
13502         vp_playfield->border_bottom = (vp_window->height -
13503                                        vp_playfield->y -
13504                                        vp_playfield->height -
13505                                        vp_playfield->margin_bottom);
13506         vp_playfield->height += vp_playfield->border_bottom;
13507       }
13508     }
13509
13510     // adjust playfield size to be a multiple of a defined alignment tile size
13511
13512     int align_size = vp_playfield->align_size;
13513     int playfield_xtiles = vp_playfield->width  / align_size;
13514     int playfield_ytiles = vp_playfield->height / align_size;
13515     int playfield_width_corrected  = playfield_xtiles * align_size;
13516     int playfield_height_corrected = playfield_ytiles * align_size;
13517     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13518                                  i == GFX_SPECIAL_ARG_EDITOR);
13519
13520     if (is_playfield_mode &&
13521         dynamic_playfield_width &&
13522         vp_playfield->width != playfield_width_corrected)
13523     {
13524       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13525
13526       vp_playfield->width = playfield_width_corrected;
13527
13528       if (vp_playfield->align == ALIGN_LEFT)
13529       {
13530         vp_playfield->border_left += playfield_xdiff;
13531       }
13532       else if (vp_playfield->align == ALIGN_RIGHT)
13533       {
13534         vp_playfield->border_right += playfield_xdiff;
13535       }
13536       else if (vp_playfield->align == ALIGN_CENTER)
13537       {
13538         int border_left_diff  = playfield_xdiff / 2;
13539         int border_right_diff = playfield_xdiff - border_left_diff;
13540
13541         vp_playfield->border_left  += border_left_diff;
13542         vp_playfield->border_right += border_right_diff;
13543       }
13544     }
13545
13546     if (is_playfield_mode &&
13547         dynamic_playfield_height &&
13548         vp_playfield->height != playfield_height_corrected)
13549     {
13550       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13551
13552       vp_playfield->height = playfield_height_corrected;
13553
13554       if (vp_playfield->valign == VALIGN_TOP)
13555       {
13556         vp_playfield->border_top += playfield_ydiff;
13557       }
13558       else if (vp_playfield->align == VALIGN_BOTTOM)
13559       {
13560         vp_playfield->border_right += playfield_ydiff;
13561       }
13562       else if (vp_playfield->align == VALIGN_MIDDLE)
13563       {
13564         int border_top_diff    = playfield_ydiff / 2;
13565         int border_bottom_diff = playfield_ydiff - border_top_diff;
13566
13567         vp_playfield->border_top    += border_top_diff;
13568         vp_playfield->border_bottom += border_bottom_diff;
13569       }
13570     }
13571
13572     // adjust door positions according to specified alignment
13573
13574     for (j = 0; j < 2; j++)
13575     {
13576       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13577
13578       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13579         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13580       else if (vp_door->align == ALIGN_CENTER)
13581         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13582       else if (vp_door->align == ALIGN_RIGHT)
13583         vp_door->x += vp_window->width - vp_door->width;
13584
13585       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13586         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13587       else if (vp_door->valign == VALIGN_MIDDLE)
13588         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13589       else if (vp_door->valign == VALIGN_BOTTOM)
13590         vp_door->y += vp_window->height - vp_door->height;
13591     }
13592   }
13593 }
13594
13595 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13596 {
13597   static struct
13598   {
13599     struct XYTileSize *dst, *src;
13600     int graphic;
13601   }
13602   editor_buttons_xy[] =
13603   {
13604     {
13605       &editor.button.element_left,      &editor.palette.element_left,
13606       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13607     },
13608     {
13609       &editor.button.element_middle,    &editor.palette.element_middle,
13610       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13611     },
13612     {
13613       &editor.button.element_right,     &editor.palette.element_right,
13614       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13615     },
13616
13617     { NULL,                     NULL                    }
13618   };
13619   int i;
13620
13621   // set default position for element buttons to element graphics
13622   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13623   {
13624     if ((*editor_buttons_xy[i].dst).x == -1 &&
13625         (*editor_buttons_xy[i].dst).y == -1)
13626     {
13627       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13628
13629       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13630
13631       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13632     }
13633   }
13634
13635   // adjust editor palette rows and columns if specified to be dynamic
13636
13637   if (editor.palette.cols == -1)
13638   {
13639     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13640     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13641     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13642
13643     editor.palette.cols = (vp_width - sc_width) / bt_width;
13644
13645     if (editor.palette.x == -1)
13646     {
13647       int palette_width = editor.palette.cols * bt_width + sc_width;
13648
13649       editor.palette.x = (vp_width - palette_width) / 2;
13650     }
13651   }
13652
13653   if (editor.palette.rows == -1)
13654   {
13655     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13656     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13657     int tx_height = getFontHeight(FONT_TEXT_2);
13658
13659     editor.palette.rows = (vp_height - tx_height) / bt_height;
13660
13661     if (editor.palette.y == -1)
13662     {
13663       int palette_height = editor.palette.rows * bt_height + tx_height;
13664
13665       editor.palette.y = (vp_height - palette_height) / 2;
13666     }
13667   }
13668 }
13669
13670 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13671                                                       boolean initialize)
13672 {
13673   // special case: check if network and preview player positions are redefined,
13674   // to compare this later against the main menu level preview being redefined
13675   struct TokenIntPtrInfo menu_config_players[] =
13676   {
13677     { "main.network_players.x", &menu.main.network_players.redefined    },
13678     { "main.network_players.y", &menu.main.network_players.redefined    },
13679     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13680     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13681     { "preview.x",              &preview.redefined                      },
13682     { "preview.y",              &preview.redefined                      }
13683   };
13684   int i;
13685
13686   if (initialize)
13687   {
13688     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13689       *menu_config_players[i].value = FALSE;
13690   }
13691   else
13692   {
13693     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13694       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13695         *menu_config_players[i].value = TRUE;
13696   }
13697 }
13698
13699 static void InitMenuDesignSettings_PreviewPlayers(void)
13700 {
13701   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13702 }
13703
13704 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13705 {
13706   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13707 }
13708
13709 static void LoadMenuDesignSettingsFromFilename(char *filename)
13710 {
13711   static struct TitleFadingInfo tfi;
13712   static struct TitleMessageInfo tmi;
13713   static struct TokenInfo title_tokens[] =
13714   {
13715     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13716     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13717     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13718     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13719     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13720
13721     { -1,               NULL,                   NULL                    }
13722   };
13723   static struct TokenInfo titlemessage_tokens[] =
13724   {
13725     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13726     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13727     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13728     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13729     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13730     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13731     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13732     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13733     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13734     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13735     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13736     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13737     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13738     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13739     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13740     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13741     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13742     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13743
13744     { -1,               NULL,                   NULL                    }
13745   };
13746   static struct
13747   {
13748     struct TitleFadingInfo *info;
13749     char *text;
13750   }
13751   title_info[] =
13752   {
13753     // initialize first titles from "enter screen" definitions, if defined
13754     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13755     { &title_first_default,             "menu.enter_screen.TITLE"       },
13756
13757     // initialize title screens from "next screen" definitions, if defined
13758     { &title_initial_default,           "menu.next_screen.TITLE"        },
13759     { &title_default,                   "menu.next_screen.TITLE"        },
13760
13761     { NULL,                             NULL                            }
13762   };
13763   static struct
13764   {
13765     struct TitleMessageInfo *array;
13766     char *text;
13767   }
13768   titlemessage_arrays[] =
13769   {
13770     // initialize first titles from "enter screen" definitions, if defined
13771     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13772     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13773     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13774     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13775
13776     // initialize titles from "next screen" definitions, if defined
13777     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13778     { titlescreen,                      "menu.next_screen.TITLE"        },
13779     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13780     { titlemessage,                     "menu.next_screen.TITLE"        },
13781
13782     // overwrite titles with title definitions, if defined
13783     { titlescreen_initial_first,        "[title_initial]"               },
13784     { titlescreen_first,                "[title]"                       },
13785     { titlemessage_initial_first,       "[title_initial]"               },
13786     { titlemessage_first,               "[title]"                       },
13787
13788     { titlescreen_initial,              "[title_initial]"               },
13789     { titlescreen,                      "[title]"                       },
13790     { titlemessage_initial,             "[title_initial]"               },
13791     { titlemessage,                     "[title]"                       },
13792
13793     // overwrite titles with title screen/message definitions, if defined
13794     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13795     { titlescreen_first,                "[titlescreen]"                 },
13796     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13797     { titlemessage_first,               "[titlemessage]"                },
13798
13799     { titlescreen_initial,              "[titlescreen_initial]"         },
13800     { titlescreen,                      "[titlescreen]"                 },
13801     { titlemessage_initial,             "[titlemessage_initial]"        },
13802     { titlemessage,                     "[titlemessage]"                },
13803
13804     { NULL,                             NULL                            }
13805   };
13806   SetupFileHash *setup_file_hash;
13807   int i, j, k;
13808
13809   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13810     return;
13811
13812   // the following initializes hierarchical values from dynamic configuration
13813
13814   // special case: initialize with default values that may be overwritten
13815   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13816   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13817   {
13818     struct TokenIntPtrInfo menu_config[] =
13819     {
13820       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13821       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13822       { "menu.list_size",       &menu.list_size[i]      }
13823     };
13824
13825     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13826     {
13827       char *token = menu_config[j].token;
13828       char *value = getHashEntry(setup_file_hash, token);
13829
13830       if (value != NULL)
13831         *menu_config[j].value = get_integer_from_string(value);
13832     }
13833   }
13834
13835   // special case: initialize with default values that may be overwritten
13836   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13837   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13838   {
13839     struct TokenIntPtrInfo menu_config[] =
13840     {
13841       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13842       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13843       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13844       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13845       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13846     };
13847
13848     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13849     {
13850       char *token = menu_config[j].token;
13851       char *value = getHashEntry(setup_file_hash, token);
13852
13853       if (value != NULL)
13854         *menu_config[j].value = get_integer_from_string(value);
13855     }
13856   }
13857
13858   // special case: initialize with default values that may be overwritten
13859   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13860   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13861   {
13862     struct TokenIntPtrInfo menu_config[] =
13863     {
13864       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13865       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13866     };
13867
13868     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13869     {
13870       char *token = menu_config[j].token;
13871       char *value = getHashEntry(setup_file_hash, token);
13872
13873       if (value != NULL)
13874         *menu_config[j].value = get_integer_from_string(value);
13875     }
13876   }
13877
13878   // special case: initialize with default values that may be overwritten
13879   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13880   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13881   {
13882     struct TokenIntPtrInfo menu_config[] =
13883     {
13884       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13885       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13886       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13887       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13888       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13889       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13890       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13891       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13892       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13893       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13894     };
13895
13896     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13897     {
13898       char *token = menu_config[j].token;
13899       char *value = getHashEntry(setup_file_hash, token);
13900
13901       if (value != NULL)
13902         *menu_config[j].value = get_integer_from_string(value);
13903     }
13904   }
13905
13906   // special case: initialize with default values that may be overwritten
13907   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13908   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13909   {
13910     struct TokenIntPtrInfo menu_config[] =
13911     {
13912       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13913       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13914       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13915       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13916       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13917       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13918       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13919       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13920       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13921     };
13922
13923     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13924     {
13925       char *token = menu_config[j].token;
13926       char *value = getHashEntry(setup_file_hash, token);
13927
13928       if (value != NULL)
13929         *menu_config[j].value = get_token_parameter_value(token, value);
13930     }
13931   }
13932
13933   // special case: initialize with default values that may be overwritten
13934   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13935   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13936   {
13937     struct
13938     {
13939       char *token_prefix;
13940       struct RectWithBorder *struct_ptr;
13941     }
13942     vp_struct[] =
13943     {
13944       { "viewport.window",      &viewport.window[i]     },
13945       { "viewport.playfield",   &viewport.playfield[i]  },
13946       { "viewport.door_1",      &viewport.door_1[i]     },
13947       { "viewport.door_2",      &viewport.door_2[i]     }
13948     };
13949
13950     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13951     {
13952       struct TokenIntPtrInfo vp_config[] =
13953       {
13954         { ".x",                 &vp_struct[j].struct_ptr->x             },
13955         { ".y",                 &vp_struct[j].struct_ptr->y             },
13956         { ".width",             &vp_struct[j].struct_ptr->width         },
13957         { ".height",            &vp_struct[j].struct_ptr->height        },
13958         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13959         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13960         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13961         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13962         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13963         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13964         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13965         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13966         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13967         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13968         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13969         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13970         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13971         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13972         { ".align",             &vp_struct[j].struct_ptr->align         },
13973         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13974       };
13975
13976       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13977       {
13978         char *token = getStringCat2(vp_struct[j].token_prefix,
13979                                     vp_config[k].token);
13980         char *value = getHashEntry(setup_file_hash, token);
13981
13982         if (value != NULL)
13983           *vp_config[k].value = get_token_parameter_value(token, value);
13984
13985         free(token);
13986       }
13987     }
13988   }
13989
13990   // special case: initialize with default values that may be overwritten
13991   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13992   for (i = 0; title_info[i].info != NULL; i++)
13993   {
13994     struct TitleFadingInfo *info = title_info[i].info;
13995     char *base_token = title_info[i].text;
13996
13997     for (j = 0; title_tokens[j].type != -1; j++)
13998     {
13999       char *token = getStringCat2(base_token, title_tokens[j].text);
14000       char *value = getHashEntry(setup_file_hash, token);
14001
14002       if (value != NULL)
14003       {
14004         int parameter_value = get_token_parameter_value(token, value);
14005
14006         tfi = *info;
14007
14008         *(int *)title_tokens[j].value = (int)parameter_value;
14009
14010         *info = tfi;
14011       }
14012
14013       free(token);
14014     }
14015   }
14016
14017   // special case: initialize with default values that may be overwritten
14018   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14019   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14020   {
14021     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14022     char *base_token = titlemessage_arrays[i].text;
14023
14024     for (j = 0; titlemessage_tokens[j].type != -1; j++)
14025     {
14026       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14027       char *value = getHashEntry(setup_file_hash, token);
14028
14029       if (value != NULL)
14030       {
14031         int parameter_value = get_token_parameter_value(token, value);
14032
14033         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14034         {
14035           tmi = array[k];
14036
14037           if (titlemessage_tokens[j].type == TYPE_INTEGER)
14038             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
14039           else
14040             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14041
14042           array[k] = tmi;
14043         }
14044       }
14045
14046       free(token);
14047     }
14048   }
14049
14050   // read (and overwrite with) values that may be specified in config file
14051   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14052
14053   // special case: check if network and preview player positions are redefined
14054   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14055
14056   freeSetupFileHash(setup_file_hash);
14057 }
14058
14059 void LoadMenuDesignSettings(void)
14060 {
14061   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14062
14063   InitMenuDesignSettings_Static();
14064   InitMenuDesignSettings_SpecialPreProcessing();
14065   InitMenuDesignSettings_PreviewPlayers();
14066
14067   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14068   {
14069     // first look for special settings configured in level series config
14070     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14071
14072     if (fileExists(filename_base))
14073       LoadMenuDesignSettingsFromFilename(filename_base);
14074   }
14075
14076   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14077
14078   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14079     LoadMenuDesignSettingsFromFilename(filename_local);
14080
14081   InitMenuDesignSettings_SpecialPostProcessing();
14082 }
14083
14084 void LoadMenuDesignSettings_AfterGraphics(void)
14085 {
14086   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14087 }
14088
14089 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14090                                 boolean ignore_defaults)
14091 {
14092   int i;
14093
14094   for (i = 0; sound_config_vars[i].token != NULL; i++)
14095   {
14096     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14097
14098     // (ignore definitions set to "[DEFAULT]" which are already initialized)
14099     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14100       continue;
14101
14102     if (value != NULL)
14103       *sound_config_vars[i].value =
14104         get_token_parameter_value(sound_config_vars[i].token, value);
14105   }
14106 }
14107
14108 void InitSoundSettings_Static(void)
14109 {
14110   // always start with reliable default values from static default config
14111   InitSoundSettings_FromHash(sound_config_hash, FALSE);
14112 }
14113
14114 static void LoadSoundSettingsFromFilename(char *filename)
14115 {
14116   SetupFileHash *setup_file_hash;
14117
14118   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14119     return;
14120
14121   // read (and overwrite with) values that may be specified in config file
14122   InitSoundSettings_FromHash(setup_file_hash, TRUE);
14123
14124   freeSetupFileHash(setup_file_hash);
14125 }
14126
14127 void LoadSoundSettings(void)
14128 {
14129   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14130
14131   InitSoundSettings_Static();
14132
14133   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14134   {
14135     // first look for special settings configured in level series config
14136     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14137
14138     if (fileExists(filename_base))
14139       LoadSoundSettingsFromFilename(filename_base);
14140   }
14141
14142   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14143
14144   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14145     LoadSoundSettingsFromFilename(filename_local);
14146 }
14147
14148 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14149 {
14150   char *filename = getEditorSetupFilename();
14151   SetupFileList *setup_file_list, *list;
14152   SetupFileHash *element_hash;
14153   int num_unknown_tokens = 0;
14154   int i;
14155
14156   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14157     return;
14158
14159   element_hash = newSetupFileHash();
14160
14161   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14162     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14163
14164   // determined size may be larger than needed (due to unknown elements)
14165   *num_elements = 0;
14166   for (list = setup_file_list; list != NULL; list = list->next)
14167     (*num_elements)++;
14168
14169   // add space for up to 3 more elements for padding that may be needed
14170   *num_elements += 3;
14171
14172   // free memory for old list of elements, if needed
14173   checked_free(*elements);
14174
14175   // allocate memory for new list of elements
14176   *elements = checked_malloc(*num_elements * sizeof(int));
14177
14178   *num_elements = 0;
14179   for (list = setup_file_list; list != NULL; list = list->next)
14180   {
14181     char *value = getHashEntry(element_hash, list->token);
14182
14183     if (value == NULL)          // try to find obsolete token mapping
14184     {
14185       char *mapped_token = get_mapped_token(list->token);
14186
14187       if (mapped_token != NULL)
14188       {
14189         value = getHashEntry(element_hash, mapped_token);
14190
14191         free(mapped_token);
14192       }
14193     }
14194
14195     if (value != NULL)
14196     {
14197       (*elements)[(*num_elements)++] = atoi(value);
14198     }
14199     else
14200     {
14201       if (num_unknown_tokens == 0)
14202       {
14203         Warn("---");
14204         Warn("unknown token(s) found in config file:");
14205         Warn("- config file: '%s'", filename);
14206
14207         num_unknown_tokens++;
14208       }
14209
14210       Warn("- token: '%s'", list->token);
14211     }
14212   }
14213
14214   if (num_unknown_tokens > 0)
14215     Warn("---");
14216
14217   while (*num_elements % 4)     // pad with empty elements, if needed
14218     (*elements)[(*num_elements)++] = EL_EMPTY;
14219
14220   freeSetupFileList(setup_file_list);
14221   freeSetupFileHash(element_hash);
14222
14223 #if 0
14224   for (i = 0; i < *num_elements; i++)
14225     Debug("editor", "element '%s' [%d]\n",
14226           element_info[(*elements)[i]].token_name, (*elements)[i]);
14227 #endif
14228 }
14229
14230 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14231                                                      boolean is_sound)
14232 {
14233   SetupFileHash *setup_file_hash = NULL;
14234   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14235   char *filename_music, *filename_prefix, *filename_info;
14236   struct
14237   {
14238     char *token;
14239     char **value_ptr;
14240   }
14241   token_to_value_ptr[] =
14242   {
14243     { "title_header",   &tmp_music_file_info.title_header       },
14244     { "artist_header",  &tmp_music_file_info.artist_header      },
14245     { "album_header",   &tmp_music_file_info.album_header       },
14246     { "year_header",    &tmp_music_file_info.year_header        },
14247     { "played_header",  &tmp_music_file_info.played_header      },
14248
14249     { "title",          &tmp_music_file_info.title              },
14250     { "artist",         &tmp_music_file_info.artist             },
14251     { "album",          &tmp_music_file_info.album              },
14252     { "year",           &tmp_music_file_info.year               },
14253     { "played",         &tmp_music_file_info.played             },
14254
14255     { NULL,             NULL                                    },
14256   };
14257   int i;
14258
14259   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14260                     getCustomMusicFilename(basename));
14261
14262   if (filename_music == NULL)
14263     return NULL;
14264
14265   // ---------- try to replace file extension ----------
14266
14267   filename_prefix = getStringCopy(filename_music);
14268   if (strrchr(filename_prefix, '.') != NULL)
14269     *strrchr(filename_prefix, '.') = '\0';
14270   filename_info = getStringCat2(filename_prefix, ".txt");
14271
14272   if (fileExists(filename_info))
14273     setup_file_hash = loadSetupFileHash(filename_info);
14274
14275   free(filename_prefix);
14276   free(filename_info);
14277
14278   if (setup_file_hash == NULL)
14279   {
14280     // ---------- try to add file extension ----------
14281
14282     filename_prefix = getStringCopy(filename_music);
14283     filename_info = getStringCat2(filename_prefix, ".txt");
14284
14285     if (fileExists(filename_info))
14286       setup_file_hash = loadSetupFileHash(filename_info);
14287
14288     free(filename_prefix);
14289     free(filename_info);
14290   }
14291
14292   if (setup_file_hash == NULL)
14293     return NULL;
14294
14295   // ---------- music file info found ----------
14296
14297   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14298
14299   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14300   {
14301     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14302
14303     *token_to_value_ptr[i].value_ptr =
14304       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14305   }
14306
14307   tmp_music_file_info.basename = getStringCopy(basename);
14308   tmp_music_file_info.music = music;
14309   tmp_music_file_info.is_sound = is_sound;
14310
14311   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14312   *new_music_file_info = tmp_music_file_info;
14313
14314   return new_music_file_info;
14315 }
14316
14317 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14318 {
14319   return get_music_file_info_ext(basename, music, FALSE);
14320 }
14321
14322 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14323 {
14324   return get_music_file_info_ext(basename, sound, TRUE);
14325 }
14326
14327 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14328                                      char *basename, boolean is_sound)
14329 {
14330   for (; list != NULL; list = list->next)
14331     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14332       return TRUE;
14333
14334   return FALSE;
14335 }
14336
14337 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14338 {
14339   return music_info_listed_ext(list, basename, FALSE);
14340 }
14341
14342 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14343 {
14344   return music_info_listed_ext(list, basename, TRUE);
14345 }
14346
14347 void LoadMusicInfo(void)
14348 {
14349   int num_music_noconf = getMusicListSize_NoConf();
14350   int num_music = getMusicListSize();
14351   int num_sounds = getSoundListSize();
14352   struct FileInfo *music, *sound;
14353   struct MusicFileInfo *next, **new;
14354
14355   int i;
14356
14357   while (music_file_info != NULL)
14358   {
14359     next = music_file_info->next;
14360
14361     checked_free(music_file_info->basename);
14362
14363     checked_free(music_file_info->title_header);
14364     checked_free(music_file_info->artist_header);
14365     checked_free(music_file_info->album_header);
14366     checked_free(music_file_info->year_header);
14367     checked_free(music_file_info->played_header);
14368
14369     checked_free(music_file_info->title);
14370     checked_free(music_file_info->artist);
14371     checked_free(music_file_info->album);
14372     checked_free(music_file_info->year);
14373     checked_free(music_file_info->played);
14374
14375     free(music_file_info);
14376
14377     music_file_info = next;
14378   }
14379
14380   new = &music_file_info;
14381
14382   // get (configured or unconfigured) music file info for all levels
14383   for (i = leveldir_current->first_level;
14384        i <= leveldir_current->last_level; i++)
14385   {
14386     int music_nr;
14387
14388     if (levelset.music[i] != MUS_UNDEFINED)
14389     {
14390       // get music file info for configured level music
14391       music_nr = levelset.music[i];
14392     }
14393     else if (num_music_noconf > 0)
14394     {
14395       // get music file info for unconfigured level music
14396       int level_pos = i - leveldir_current->first_level;
14397
14398       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14399     }
14400     else
14401     {
14402       continue;
14403     }
14404
14405     char *basename = getMusicInfoEntryFilename(music_nr);
14406
14407     if (basename == NULL)
14408       continue;
14409
14410     if (!music_info_listed(music_file_info, basename))
14411     {
14412       *new = get_music_file_info(basename, music_nr);
14413
14414       if (*new != NULL)
14415         new = &(*new)->next;
14416     }
14417   }
14418
14419   // get music file info for all remaining configured music files
14420   for (i = 0; i < num_music; i++)
14421   {
14422     music = getMusicListEntry(i);
14423
14424     if (music->filename == NULL)
14425       continue;
14426
14427     if (strEqual(music->filename, UNDEFINED_FILENAME))
14428       continue;
14429
14430     // a configured file may be not recognized as music
14431     if (!FileIsMusic(music->filename))
14432       continue;
14433
14434     if (!music_info_listed(music_file_info, music->filename))
14435     {
14436       *new = get_music_file_info(music->filename, i);
14437
14438       if (*new != NULL)
14439         new = &(*new)->next;
14440     }
14441   }
14442
14443   // get sound file info for all configured sound files
14444   for (i = 0; i < num_sounds; i++)
14445   {
14446     sound = getSoundListEntry(i);
14447
14448     if (sound->filename == NULL)
14449       continue;
14450
14451     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14452       continue;
14453
14454     // a configured file may be not recognized as sound
14455     if (!FileIsSound(sound->filename))
14456       continue;
14457
14458     if (!sound_info_listed(music_file_info, sound->filename))
14459     {
14460       *new = get_sound_file_info(sound->filename, i);
14461       if (*new != NULL)
14462         new = &(*new)->next;
14463     }
14464   }
14465
14466   // add pointers to previous list nodes
14467
14468   struct MusicFileInfo *node = music_file_info;
14469
14470   while (node != NULL)
14471   {
14472     if (node->next)
14473       node->next->prev = node;
14474
14475     node = node->next;
14476   }
14477 }
14478
14479 static void add_helpanim_entry(int element, int action, int direction,
14480                                int delay, int *num_list_entries)
14481 {
14482   struct HelpAnimInfo *new_list_entry;
14483   (*num_list_entries)++;
14484
14485   helpanim_info =
14486     checked_realloc(helpanim_info,
14487                     *num_list_entries * sizeof(struct HelpAnimInfo));
14488   new_list_entry = &helpanim_info[*num_list_entries - 1];
14489
14490   new_list_entry->element = element;
14491   new_list_entry->action = action;
14492   new_list_entry->direction = direction;
14493   new_list_entry->delay = delay;
14494 }
14495
14496 static void print_unknown_token(char *filename, char *token, int token_nr)
14497 {
14498   if (token_nr == 0)
14499   {
14500     Warn("---");
14501     Warn("unknown token(s) found in config file:");
14502     Warn("- config file: '%s'", filename);
14503   }
14504
14505   Warn("- token: '%s'", token);
14506 }
14507
14508 static void print_unknown_token_end(int token_nr)
14509 {
14510   if (token_nr > 0)
14511     Warn("---");
14512 }
14513
14514 void LoadHelpAnimInfo(void)
14515 {
14516   char *filename = getHelpAnimFilename();
14517   SetupFileList *setup_file_list = NULL, *list;
14518   SetupFileHash *element_hash, *action_hash, *direction_hash;
14519   int num_list_entries = 0;
14520   int num_unknown_tokens = 0;
14521   int i;
14522
14523   if (fileExists(filename))
14524     setup_file_list = loadSetupFileList(filename);
14525
14526   if (setup_file_list == NULL)
14527   {
14528     // use reliable default values from static configuration
14529     SetupFileList *insert_ptr;
14530
14531     insert_ptr = setup_file_list =
14532       newSetupFileList(helpanim_config[0].token,
14533                        helpanim_config[0].value);
14534
14535     for (i = 1; helpanim_config[i].token; i++)
14536       insert_ptr = addListEntry(insert_ptr,
14537                                 helpanim_config[i].token,
14538                                 helpanim_config[i].value);
14539   }
14540
14541   element_hash   = newSetupFileHash();
14542   action_hash    = newSetupFileHash();
14543   direction_hash = newSetupFileHash();
14544
14545   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14546     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14547
14548   for (i = 0; i < NUM_ACTIONS; i++)
14549     setHashEntry(action_hash, element_action_info[i].suffix,
14550                  i_to_a(element_action_info[i].value));
14551
14552   // do not store direction index (bit) here, but direction value!
14553   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14554     setHashEntry(direction_hash, element_direction_info[i].suffix,
14555                  i_to_a(1 << element_direction_info[i].value));
14556
14557   for (list = setup_file_list; list != NULL; list = list->next)
14558   {
14559     char *element_token, *action_token, *direction_token;
14560     char *element_value, *action_value, *direction_value;
14561     int delay = atoi(list->value);
14562
14563     if (strEqual(list->token, "end"))
14564     {
14565       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14566
14567       continue;
14568     }
14569
14570     /* first try to break element into element/action/direction parts;
14571        if this does not work, also accept combined "element[.act][.dir]"
14572        elements (like "dynamite.active"), which are unique elements */
14573
14574     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14575     {
14576       element_value = getHashEntry(element_hash, list->token);
14577       if (element_value != NULL)        // element found
14578         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14579                            &num_list_entries);
14580       else
14581       {
14582         // no further suffixes found -- this is not an element
14583         print_unknown_token(filename, list->token, num_unknown_tokens++);
14584       }
14585
14586       continue;
14587     }
14588
14589     // token has format "<prefix>.<something>"
14590
14591     action_token = strchr(list->token, '.');    // suffix may be action ...
14592     direction_token = action_token;             // ... or direction
14593
14594     element_token = getStringCopy(list->token);
14595     *strchr(element_token, '.') = '\0';
14596
14597     element_value = getHashEntry(element_hash, element_token);
14598
14599     if (element_value == NULL)          // this is no element
14600     {
14601       element_value = getHashEntry(element_hash, list->token);
14602       if (element_value != NULL)        // combined element found
14603         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14604                            &num_list_entries);
14605       else
14606         print_unknown_token(filename, list->token, num_unknown_tokens++);
14607
14608       free(element_token);
14609
14610       continue;
14611     }
14612
14613     action_value = getHashEntry(action_hash, action_token);
14614
14615     if (action_value != NULL)           // action found
14616     {
14617       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14618                     &num_list_entries);
14619
14620       free(element_token);
14621
14622       continue;
14623     }
14624
14625     direction_value = getHashEntry(direction_hash, direction_token);
14626
14627     if (direction_value != NULL)        // direction found
14628     {
14629       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14630                          &num_list_entries);
14631
14632       free(element_token);
14633
14634       continue;
14635     }
14636
14637     if (strchr(action_token + 1, '.') == NULL)
14638     {
14639       // no further suffixes found -- this is not an action nor direction
14640
14641       element_value = getHashEntry(element_hash, list->token);
14642       if (element_value != NULL)        // combined element found
14643         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14644                            &num_list_entries);
14645       else
14646         print_unknown_token(filename, list->token, num_unknown_tokens++);
14647
14648       free(element_token);
14649
14650       continue;
14651     }
14652
14653     // token has format "<prefix>.<suffix>.<something>"
14654
14655     direction_token = strchr(action_token + 1, '.');
14656
14657     action_token = getStringCopy(action_token);
14658     *strchr(action_token + 1, '.') = '\0';
14659
14660     action_value = getHashEntry(action_hash, action_token);
14661
14662     if (action_value == NULL)           // this is no action
14663     {
14664       element_value = getHashEntry(element_hash, list->token);
14665       if (element_value != NULL)        // combined element found
14666         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14667                            &num_list_entries);
14668       else
14669         print_unknown_token(filename, list->token, num_unknown_tokens++);
14670
14671       free(element_token);
14672       free(action_token);
14673
14674       continue;
14675     }
14676
14677     direction_value = getHashEntry(direction_hash, direction_token);
14678
14679     if (direction_value != NULL)        // direction found
14680     {
14681       add_helpanim_entry(atoi(element_value), atoi(action_value),
14682                          atoi(direction_value), delay, &num_list_entries);
14683
14684       free(element_token);
14685       free(action_token);
14686
14687       continue;
14688     }
14689
14690     // this is no direction
14691
14692     element_value = getHashEntry(element_hash, list->token);
14693     if (element_value != NULL)          // combined element found
14694       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14695                          &num_list_entries);
14696     else
14697       print_unknown_token(filename, list->token, num_unknown_tokens++);
14698
14699     free(element_token);
14700     free(action_token);
14701   }
14702
14703   print_unknown_token_end(num_unknown_tokens);
14704
14705   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14706   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14707
14708   freeSetupFileList(setup_file_list);
14709   freeSetupFileHash(element_hash);
14710   freeSetupFileHash(action_hash);
14711   freeSetupFileHash(direction_hash);
14712
14713 #if 0
14714   for (i = 0; i < num_list_entries; i++)
14715     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14716           EL_NAME(helpanim_info[i].element),
14717           helpanim_info[i].element,
14718           helpanim_info[i].action,
14719           helpanim_info[i].direction,
14720           helpanim_info[i].delay);
14721 #endif
14722 }
14723
14724 void LoadHelpTextInfo(void)
14725 {
14726   char *filename = getHelpTextFilename();
14727   int i;
14728
14729   if (helptext_info != NULL)
14730   {
14731     freeSetupFileHash(helptext_info);
14732     helptext_info = NULL;
14733   }
14734
14735   if (fileExists(filename))
14736     helptext_info = loadSetupFileHash(filename);
14737
14738   if (helptext_info == NULL)
14739   {
14740     // use reliable default values from static configuration
14741     helptext_info = newSetupFileHash();
14742
14743     for (i = 0; helptext_config[i].token; i++)
14744       setHashEntry(helptext_info,
14745                    helptext_config[i].token,
14746                    helptext_config[i].value);
14747   }
14748
14749 #if 0
14750   BEGIN_HASH_ITERATION(helptext_info, itr)
14751   {
14752     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14753           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14754   }
14755   END_HASH_ITERATION(hash, itr)
14756 #endif
14757 }
14758
14759
14760 // ----------------------------------------------------------------------------
14761 // convert levels
14762 // ----------------------------------------------------------------------------
14763
14764 #define MAX_NUM_CONVERT_LEVELS          1000
14765
14766 void ConvertLevels(void)
14767 {
14768   static LevelDirTree *convert_leveldir = NULL;
14769   static int convert_level_nr = -1;
14770   static int num_levels_handled = 0;
14771   static int num_levels_converted = 0;
14772   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14773   int i;
14774
14775   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14776                                                global.convert_leveldir);
14777
14778   if (convert_leveldir == NULL)
14779     Fail("no such level identifier: '%s'", global.convert_leveldir);
14780
14781   leveldir_current = convert_leveldir;
14782
14783   if (global.convert_level_nr != -1)
14784   {
14785     convert_leveldir->first_level = global.convert_level_nr;
14786     convert_leveldir->last_level  = global.convert_level_nr;
14787   }
14788
14789   convert_level_nr = convert_leveldir->first_level;
14790
14791   PrintLine("=", 79);
14792   Print("Converting levels\n");
14793   PrintLine("-", 79);
14794   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14795   Print("Level series name:       '%s'\n", convert_leveldir->name);
14796   Print("Level series author:     '%s'\n", convert_leveldir->author);
14797   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14798   PrintLine("=", 79);
14799   Print("\n");
14800
14801   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14802     levels_failed[i] = FALSE;
14803
14804   while (convert_level_nr <= convert_leveldir->last_level)
14805   {
14806     char *level_filename;
14807     boolean new_level;
14808
14809     level_nr = convert_level_nr++;
14810
14811     Print("Level %03d: ", level_nr);
14812
14813     LoadLevel(level_nr);
14814     if (level.no_level_file || level.no_valid_file)
14815     {
14816       Print("(no level)\n");
14817       continue;
14818     }
14819
14820     Print("converting level ... ");
14821
14822 #if 0
14823     // special case: conversion of some EMC levels as requested by ACME
14824     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14825 #endif
14826
14827     level_filename = getDefaultLevelFilename(level_nr);
14828     new_level = !fileExists(level_filename);
14829
14830     if (new_level)
14831     {
14832       SaveLevel(level_nr);
14833
14834       num_levels_converted++;
14835
14836       Print("converted.\n");
14837     }
14838     else
14839     {
14840       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14841         levels_failed[level_nr] = TRUE;
14842
14843       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14844     }
14845
14846     num_levels_handled++;
14847   }
14848
14849   Print("\n");
14850   PrintLine("=", 79);
14851   Print("Number of levels handled: %d\n", num_levels_handled);
14852   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14853          (num_levels_handled ?
14854           num_levels_converted * 100 / num_levels_handled : 0));
14855   PrintLine("-", 79);
14856   Print("Summary (for automatic parsing by scripts):\n");
14857   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14858          convert_leveldir->identifier, num_levels_converted,
14859          num_levels_handled,
14860          (num_levels_handled ?
14861           num_levels_converted * 100 / num_levels_handled : 0));
14862
14863   if (num_levels_handled != num_levels_converted)
14864   {
14865     Print(", FAILED:");
14866     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14867       if (levels_failed[i])
14868         Print(" %03d", i);
14869   }
14870
14871   Print("\n");
14872   PrintLine("=", 79);
14873
14874   CloseAllAndExit(0);
14875 }
14876
14877
14878 // ----------------------------------------------------------------------------
14879 // create and save images for use in level sketches (raw BMP format)
14880 // ----------------------------------------------------------------------------
14881
14882 void CreateLevelSketchImages(void)
14883 {
14884   Bitmap *bitmap1;
14885   Bitmap *bitmap2;
14886   int i;
14887
14888   InitElementPropertiesGfxElement();
14889
14890   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14891   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14892
14893   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14894   {
14895     int element = getMappedElement(i);
14896     char basename1[16];
14897     char basename2[16];
14898     char *filename1;
14899     char *filename2;
14900
14901     sprintf(basename1, "%04d.bmp", i);
14902     sprintf(basename2, "%04ds.bmp", i);
14903
14904     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14905     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14906
14907     DrawSizedElement(0, 0, element, TILESIZE);
14908     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14909
14910     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14911       Fail("cannot save level sketch image file '%s'", filename1);
14912
14913     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14914     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14915
14916     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14917       Fail("cannot save level sketch image file '%s'", filename2);
14918
14919     free(filename1);
14920     free(filename2);
14921
14922     // create corresponding SQL statements (for normal and small images)
14923     if (i < 1000)
14924     {
14925       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14926       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14927     }
14928
14929     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14930     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14931
14932     // optional: create content for forum level sketch demonstration post
14933     if (options.debug)
14934       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14935   }
14936
14937   FreeBitmap(bitmap1);
14938   FreeBitmap(bitmap2);
14939
14940   if (options.debug)
14941     fprintf(stderr, "\n");
14942
14943   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14944
14945   CloseAllAndExit(0);
14946 }
14947
14948
14949 // ----------------------------------------------------------------------------
14950 // create and save images for element collecting animations (raw BMP format)
14951 // ----------------------------------------------------------------------------
14952
14953 static boolean createCollectImage(int element)
14954 {
14955   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14956 }
14957
14958 void CreateCollectElementImages(void)
14959 {
14960   int i, j;
14961   int num_steps = 8;
14962   int anim_frames = num_steps - 1;
14963   int tile_size = TILESIZE;
14964   int anim_width  = tile_size * anim_frames;
14965   int anim_height = tile_size;
14966   int num_collect_images = 0;
14967   int pos_collect_images = 0;
14968
14969   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14970     if (createCollectImage(i))
14971       num_collect_images++;
14972
14973   Info("Creating %d element collecting animation images ...",
14974        num_collect_images);
14975
14976   int dst_width  = anim_width * 2;
14977   int dst_height = anim_height * num_collect_images / 2;
14978   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14979   char *basename_bmp = "RocksCollect.bmp";
14980   char *basename_png = "RocksCollect.png";
14981   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14982   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14983   int len_filename_bmp = strlen(filename_bmp);
14984   int len_filename_png = strlen(filename_png);
14985   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14986   char cmd_convert[max_command_len];
14987
14988   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14989            filename_bmp,
14990            filename_png);
14991
14992   // force using RGBA surface for destination bitmap
14993   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14994                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14995
14996   dst_bitmap->surface =
14997     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14998
14999   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15000   {
15001     if (!createCollectImage(i))
15002       continue;
15003
15004     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15005     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15006     int graphic = el2img(i);
15007     char *token_name = element_info[i].token_name;
15008     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15009     Bitmap *src_bitmap;
15010     int src_x, src_y;
15011
15012     Info("- creating collecting image for '%s' ...", token_name);
15013
15014     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15015
15016     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15017                tile_size, tile_size, 0, 0);
15018
15019     // force using RGBA surface for temporary bitmap (using transparent black)
15020     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15021                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15022
15023     tmp_bitmap->surface =
15024       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15025
15026     tmp_bitmap->surface_masked = tmp_bitmap->surface;
15027
15028     for (j = 0; j < anim_frames; j++)
15029     {
15030       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15031       int frame_size = frame_size_final * num_steps;
15032       int offset = (tile_size - frame_size_final) / 2;
15033       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15034
15035       while (frame_size > frame_size_final)
15036       {
15037         frame_size /= 2;
15038
15039         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15040
15041         FreeBitmap(frame_bitmap);
15042
15043         frame_bitmap = half_bitmap;
15044       }
15045
15046       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15047                        frame_size_final, frame_size_final,
15048                        dst_x + j * tile_size + offset, dst_y + offset);
15049
15050       FreeBitmap(frame_bitmap);
15051     }
15052
15053     tmp_bitmap->surface_masked = NULL;
15054
15055     FreeBitmap(tmp_bitmap);
15056
15057     pos_collect_images++;
15058   }
15059
15060   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15061     Fail("cannot save element collecting image file '%s'", filename_bmp);
15062
15063   FreeBitmap(dst_bitmap);
15064
15065   Info("Converting image file from BMP to PNG ...");
15066
15067   if (system(cmd_convert) != 0)
15068     Fail("converting image file failed");
15069
15070   unlink(filename_bmp);
15071
15072   Info("Done.");
15073
15074   CloseAllAndExit(0);
15075 }
15076
15077
15078 // ----------------------------------------------------------------------------
15079 // create and save images for custom and group elements (raw BMP format)
15080 // ----------------------------------------------------------------------------
15081
15082 void CreateCustomElementImages(char *directory)
15083 {
15084   char *src_basename = "RocksCE-template.ilbm";
15085   char *dst_basename = "RocksCE.bmp";
15086   char *src_filename = getPath2(directory, src_basename);
15087   char *dst_filename = getPath2(directory, dst_basename);
15088   Bitmap *src_bitmap;
15089   Bitmap *bitmap;
15090   int yoffset_ce = 0;
15091   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15092   int i;
15093
15094   InitVideoDefaults();
15095
15096   ReCreateBitmap(&backbuffer, video.width, video.height);
15097
15098   src_bitmap = LoadImage(src_filename);
15099
15100   bitmap = CreateBitmap(TILEX * 16 * 2,
15101                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15102                         DEFAULT_DEPTH);
15103
15104   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15105   {
15106     int x = i % 16;
15107     int y = i / 16;
15108     int ii = i + 1;
15109     int j;
15110
15111     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15112                TILEX * x, TILEY * y + yoffset_ce);
15113
15114     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15115                TILEX, TILEY,
15116                TILEX * x + TILEX * 16,
15117                TILEY * y + yoffset_ce);
15118
15119     for (j = 2; j >= 0; j--)
15120     {
15121       int c = ii % 10;
15122
15123       BlitBitmap(src_bitmap, bitmap,
15124                  TILEX + c * 7, 0, 6, 10,
15125                  TILEX * x + 6 + j * 7,
15126                  TILEY * y + 11 + yoffset_ce);
15127
15128       BlitBitmap(src_bitmap, bitmap,
15129                  TILEX + c * 8, TILEY, 6, 10,
15130                  TILEX * 16 + TILEX * x + 6 + j * 8,
15131                  TILEY * y + 10 + yoffset_ce);
15132
15133       ii /= 10;
15134     }
15135   }
15136
15137   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15138   {
15139     int x = i % 16;
15140     int y = i / 16;
15141     int ii = i + 1;
15142     int j;
15143
15144     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15145                TILEX * x, TILEY * y + yoffset_ge);
15146
15147     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15148                TILEX, TILEY,
15149                TILEX * x + TILEX * 16,
15150                TILEY * y + yoffset_ge);
15151
15152     for (j = 1; j >= 0; j--)
15153     {
15154       int c = ii % 10;
15155
15156       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15157                  TILEX * x + 6 + j * 10,
15158                  TILEY * y + 11 + yoffset_ge);
15159
15160       BlitBitmap(src_bitmap, bitmap,
15161                  TILEX + c * 8, TILEY + 12, 6, 10,
15162                  TILEX * 16 + TILEX * x + 10 + j * 8,
15163                  TILEY * y + 10 + yoffset_ge);
15164
15165       ii /= 10;
15166     }
15167   }
15168
15169   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15170     Fail("cannot save CE graphics file '%s'", dst_filename);
15171
15172   FreeBitmap(bitmap);
15173
15174   CloseAllAndExit(0);
15175 }