moved code to separate function
[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_BDX_PLAYER,                      -1,
644     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
645     &li.bd_diagonal_movements,          FALSE
646   },
647   {
648     EL_BDX_PLAYER,                      -1,
649     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
650     &li.bd_topmost_player_active,       TRUE
651   },
652   {
653     EL_BDX_PLAYER,                      -1,
654     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
655     &li.bd_pushing_prob,                25
656   },
657   {
658     EL_BDX_PLAYER,                      -1,
659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
660     &li.bd_pushing_prob_with_sweet,     100
661   },
662   {
663     EL_BDX_PLAYER,                      -1,
664     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
665     &li.bd_push_mega_rock_with_sweet,   FALSE
666   },
667   {
668     EL_BDX_PLAYER,                      -1,
669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
670     &li.bd_snap_element,                EL_EMPTY
671   },
672
673   {
674     EL_BDX_SAND_1,                      -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
676     &li.bd_sand_looks_like,             EL_BDX_SAND_1
677   },
678
679   {
680     EL_BDX_ROCK,                        -1,
681     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
682     &li.bd_rock_turns_to_on_falling,    EL_BDX_ROCK_FALLING
683   },
684   {
685     EL_BDX_ROCK,                        -1,
686     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
687     &li.bd_rock_turns_to_on_impact,     EL_BDX_ROCK
688   },
689
690   {
691     EL_BDX_DIAMOND,                     -1,
692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
693     &li.score[SC_DIAMOND_EXTRA],        20
694   },
695   {
696     EL_BDX_DIAMOND,                     -1,
697     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
698     &li.bd_diamond_turns_to_on_falling, EL_BDX_DIAMOND_FALLING
699   },
700   {
701     EL_BDX_DIAMOND,                     -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
703     &li.bd_diamond_turns_to_on_impact,  EL_BDX_DIAMOND
704   },
705
706   {
707     EL_BDX_FIREFLY_1,                   -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_firefly_1_explodes_to,       EL_BDX_EXPLODING_1
710   },
711
712   {
713     EL_BDX_FIREFLY_2,                   -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_firefly_2_explodes_to,       EL_BDX_EXPLODING_1
716   },
717
718   {
719     EL_BDX_BUTTERFLY_1,                 -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_butterfly_1_explodes_to,     EL_BDX_DIAMOND_GROWING_1
722   },
723
724   {
725     EL_BDX_BUTTERFLY_2,                 -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_butterfly_2_explodes_to,     EL_BDX_DIAMOND_GROWING_1
728   },
729
730   {
731     EL_BDX_STONEFLY,                    -1,
732     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
733     &li.bd_stonefly_explodes_to,        EL_BDX_ROCK_GROWING_1
734   },
735
736   {
737     EL_BDX_DRAGONFLY,                   -1,
738     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
739     &li.bd_dragonfly_explodes_to,       EL_BDX_EXPLODING_1
740   },
741
742   {
743     EL_BDX_DIAMOND_GROWING_5,   -1,
744     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
745     &li.bd_diamond_birth_turns_to,      EL_BDX_DIAMOND
746   },
747
748   {
749     EL_BDX_BOMB_EXPLODING_4,            -1,
750     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
751     &li.bd_bomb_explosion_turns_to,     EL_BDX_WALL
752   },
753
754   {
755     EL_BDX_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_BDX_EXPLODING_5,                 -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
763     &li.bd_explosion_turns_to,          EL_EMPTY
764   },
765
766   {
767     EL_BDX_MAGIC_WALL,                  -1,
768     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
769     &li.bd_magic_wall_wait_hatching,    FALSE
770   },
771   {
772     EL_BDX_MAGIC_WALL,                  -1,
773     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
774     &li.bd_magic_wall_stops_amoeba,     TRUE
775   },
776   {
777     EL_BDX_MAGIC_WALL,                  -1,
778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
779     &li.bd_magic_wall_zero_infinite,    TRUE
780   },
781   {
782     EL_BDX_MAGIC_WALL,                  -1,
783     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
784     &li.bd_magic_wall_break_scan,       FALSE
785   },
786   {
787     EL_BDX_MAGIC_WALL,                  -1,
788     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
789     &li.bd_magic_wall_time,             999
790   },
791   {
792     EL_BDX_MAGIC_WALL,                  -1,
793     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
794     &li.bd_magic_wall_diamond_to,       EL_BDX_ROCK_FALLING
795   },
796   {
797     EL_BDX_MAGIC_WALL,                  -1,
798     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
799     &li.bd_magic_wall_rock_to,          EL_BDX_DIAMOND_FALLING
800   },
801   {
802     EL_BDX_MAGIC_WALL,                  -1,
803     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
804     &li.bd_magic_wall_mega_rock_to,     EL_BDX_NITRO_PACK_FALLING
805   },
806   {
807     EL_BDX_MAGIC_WALL,                  -1,
808     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
809     &li.bd_magic_wall_nut_to,           EL_BDX_NUT_FALLING
810   },
811   {
812     EL_BDX_MAGIC_WALL,                  -1,
813     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
814     &li.bd_magic_wall_nitro_pack_to,    EL_BDX_MEGA_ROCK_FALLING
815   },
816   {
817     EL_BDX_MAGIC_WALL,                  -1,
818     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
819     &li.bd_magic_wall_flying_diamond_to, EL_BDX_FLYING_ROCK_FLYING
820   },
821   {
822     EL_BDX_MAGIC_WALL,                  -1,
823     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
824     &li.bd_magic_wall_flying_rock_to,   EL_BDX_FLYING_DIAMOND_FLYING
825   },
826
827   {
828     EL_BDX_CLOCK,                       -1,
829     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
830     &li.bd_clock_extra_time,            30
831   },
832
833   {
834     EL_BDX_VOODOO_DOLL,                 -1,
835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
836     &li.bd_voodoo_collects_diamonds,    FALSE
837   },
838   {
839     EL_BDX_VOODOO_DOLL,                 -1,
840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
841     &li.bd_voodoo_hurt_kills_player,    FALSE
842   },
843   {
844     EL_BDX_VOODOO_DOLL,                 -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
846     &li.bd_voodoo_dies_by_rock,         FALSE
847   },
848   {
849     EL_BDX_VOODOO_DOLL,                 -1,
850     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
851     &li.bd_voodoo_vanish_by_explosion,  TRUE
852   },
853   {
854     EL_BDX_VOODOO_DOLL,                 -1,
855     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
856     &li.bd_voodoo_penalty_time,         30
857   },
858
859   {
860     EL_BDX_SLIME,                       -1,
861     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
862     &li.bd_slime_is_predictable,        TRUE
863   },
864   {
865     EL_BDX_SLIME,                       -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
867     &li.bd_slime_permeability_rate,     100
868   },
869   {
870     EL_BDX_SLIME,                       -1,
871     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
872     &li.bd_slime_permeability_bits_c64, 0
873   },
874   {
875     EL_BDX_SLIME,                       -1,
876     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
877     &li.bd_slime_random_seed_c64,       -1
878   },
879   {
880     EL_BDX_SLIME,                       -1,
881     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
882     &li.bd_slime_eats_element_1,        EL_BDX_DIAMOND
883   },
884   {
885     EL_BDX_SLIME,                       -1,
886     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
887     &li.bd_slime_converts_to_element_1, EL_BDX_DIAMOND_FALLING
888   },
889   {
890     EL_BDX_SLIME,                       -1,
891     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
892     &li.bd_slime_eats_element_2,        EL_BDX_ROCK
893   },
894   {
895     EL_BDX_SLIME,                       -1,
896     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
897     &li.bd_slime_converts_to_element_2, EL_BDX_ROCK_FALLING
898   },
899   {
900     EL_BDX_SLIME,                       -1,
901     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
902     &li.bd_slime_eats_element_3,        EL_BDX_NUT
903   },
904   {
905     EL_BDX_SLIME,                       -1,
906     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
907     &li.bd_slime_converts_to_element_3, EL_BDX_NUT_FALLING
908   },
909
910   {
911     EL_BDX_ACID,                        -1,
912     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
913     &li.bd_acid_eats_element,           EL_BDX_SAND_1
914   },
915   {
916     EL_BDX_ACID,                        -1,
917     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
918     &li.bd_acid_spread_rate,            3
919   },
920   {
921     EL_BDX_ACID,                        -1,
922     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
923     &li.bd_acid_turns_to_element,       EL_BDX_EXPLODING_3
924   },
925
926   {
927     EL_BDX_BITER,                       -1,
928     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
929     &li.bd_biter_move_delay,            0
930   },
931   {
932     EL_BDX_BITER,                       -1,
933     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
934     &li.bd_biter_eats_element,          EL_BDX_DIAMOND
935   },
936
937   {
938     EL_BDX_BLADDER,                     -1,
939     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
940     &li.bd_bladder_converts_by_element, EL_BDX_VOODOO_DOLL
941   },
942
943   {
944     EL_BDX_EXPANDABLE_WALL_ANY,         -1,
945     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
946     &li.bd_change_expanding_wall,       FALSE
947   },
948   {
949     EL_BDX_EXPANDABLE_WALL_ANY,         -1,
950     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
951     &li.bd_expanding_wall_looks_like,   EL_BDX_WALL
952   },
953
954   {
955     EL_BDX_REPLICATOR,                  -1,
956     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
957     &li.bd_replicators_active,          TRUE
958   },
959   {
960     EL_BDX_REPLICATOR,                  -1,
961     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
962     &li.bd_replicator_create_delay,     4
963   },
964
965   {
966     EL_BDX_CONVEYOR_LEFT,               -1,
967     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
968     &li.bd_conveyor_belts_active,       TRUE
969   },
970   {
971     EL_BDX_CONVEYOR_LEFT,               -1,
972     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
973     &li.bd_conveyor_belts_changed,      FALSE
974   },
975
976   {
977     EL_BDX_WATER,                       -1,
978     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
979     &li.bd_water_cannot_flow_down,      FALSE
980   },
981
982   {
983     EL_BDX_NUT,                         -1,
984     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
985     &li.bd_nut_content,                 EL_BDX_NUT_BREAKING_1
986   },
987
988   {
989     EL_BDX_PNEUMATIC_HAMMER,            -1,
990     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
991     &li.bd_hammer_walls_break_delay,    5
992   },
993   {
994     EL_BDX_PNEUMATIC_HAMMER,            -1,
995     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
996     &li.bd_hammer_walls_reappear,       FALSE
997   },
998   {
999     EL_BDX_PNEUMATIC_HAMMER,            -1,
1000     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1001     &li.bd_hammer_walls_reappear_delay, 100
1002   },
1003
1004   {
1005     EL_BDX_ROCKET_LAUNCHER,             -1,
1006     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1007     &li.bd_infinite_rockets,            FALSE
1008   },
1009
1010   {
1011     EL_BDX_SKELETON,                    -1,
1012     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1013     &li.bd_num_skeletons_needed_for_pot, 5
1014   },
1015   {
1016     EL_BDX_SKELETON,                    -1,
1017     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1018     &li.bd_skeleton_worth_num_diamonds, 0
1019   },
1020
1021   {
1022     EL_BDX_CREATURE_SWITCH,             -1,
1023     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1024     &li.bd_creatures_start_backwards,   FALSE
1025   },
1026   {
1027     EL_BDX_CREATURE_SWITCH,             -1,
1028     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1029     &li.bd_creatures_turn_on_hatching,  FALSE
1030   },
1031   {
1032     EL_BDX_CREATURE_SWITCH,             -1,
1033     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1034     &li.bd_creatures_auto_turn_delay,   0
1035   },
1036
1037   {
1038     EL_BDX_GRAVITY_SWITCH,              -1,
1039     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1040     &li.bd_gravity_direction,           GD_MV_DOWN
1041   },
1042   {
1043     EL_BDX_GRAVITY_SWITCH,              -1,
1044     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1045     &li.bd_gravity_switch_active,       FALSE
1046   },
1047   {
1048     EL_BDX_GRAVITY_SWITCH,              -1,
1049     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1050     &li.bd_gravity_switch_delay,        10
1051   },
1052   {
1053     EL_BDX_GRAVITY_SWITCH,              -1,
1054     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1055     &li.bd_gravity_affects_all,         TRUE
1056   },
1057
1058   // (the following values are related to various game elements)
1059
1060   {
1061     EL_EMERALD,                         -1,
1062     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1063     &li.score[SC_EMERALD],              10
1064   },
1065
1066   {
1067     EL_DIAMOND,                         -1,
1068     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1069     &li.score[SC_DIAMOND],              10
1070   },
1071
1072   {
1073     EL_BUG,                             -1,
1074     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1075     &li.score[SC_BUG],                  10
1076   },
1077
1078   {
1079     EL_SPACESHIP,                       -1,
1080     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1081     &li.score[SC_SPACESHIP],            10
1082   },
1083
1084   {
1085     EL_PACMAN,                          -1,
1086     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1087     &li.score[SC_PACMAN],               10
1088   },
1089
1090   {
1091     EL_NUT,                             -1,
1092     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1093     &li.score[SC_NUT],                  10
1094   },
1095
1096   {
1097     EL_DYNAMITE,                        -1,
1098     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1099     &li.score[SC_DYNAMITE],             10
1100   },
1101
1102   {
1103     EL_KEY_1,                           -1,
1104     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1105     &li.score[SC_KEY],                  10
1106   },
1107
1108   {
1109     EL_PEARL,                           -1,
1110     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1111     &li.score[SC_PEARL],                10
1112   },
1113
1114   {
1115     EL_CRYSTAL,                         -1,
1116     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1117     &li.score[SC_CRYSTAL],              10
1118   },
1119
1120   {
1121     EL_BD_AMOEBA,                       -1,
1122     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1123     &li.amoeba_content,                 EL_DIAMOND
1124   },
1125   {
1126     EL_BD_AMOEBA,                       -1,
1127     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1128     &li.amoeba_speed,                   10
1129   },
1130   {
1131     EL_BD_AMOEBA,                       -1,
1132     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1133     &li.grow_into_diggable,             TRUE
1134   },
1135
1136   {
1137     EL_BDX_AMOEBA_1,                    -1,
1138     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1139     &li.bd_amoeba_1_threshold_too_big,  200
1140   },
1141   {
1142     EL_BDX_AMOEBA_1,                    -1,
1143     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1144     &li.bd_amoeba_1_slow_growth_time,   200
1145   },
1146   {
1147     EL_BDX_AMOEBA_1,                    -1,
1148     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1149     &li.bd_amoeba_1_content_too_big,    EL_BDX_ROCK
1150   },
1151   {
1152     EL_BDX_AMOEBA_1,                    -1,
1153     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
1154     &li.bd_amoeba_1_content_enclosed,   EL_BDX_DIAMOND
1155   },
1156   {
1157     EL_BDX_AMOEBA_1,                    -1,
1158     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1159     &li.bd_amoeba_1_slow_growth_rate,   3
1160   },
1161   {
1162     EL_BDX_AMOEBA_1,                    -1,
1163     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1164     &li.bd_amoeba_1_fast_growth_rate,   25
1165   },
1166   {
1167     EL_BDX_AMOEBA_1,                    -1,
1168     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1169     &li.bd_amoeba_wait_for_hatching,    FALSE
1170   },
1171   {
1172     EL_BDX_AMOEBA_1,                    -1,
1173     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1174     &li.bd_amoeba_start_immediately,    TRUE
1175   },
1176
1177   {
1178     EL_BDX_AMOEBA_2,                    -1,
1179     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1180     &li.bd_amoeba_2_threshold_too_big,  200
1181   },
1182   {
1183     EL_BDX_AMOEBA_2,                    -1,
1184     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1185     &li.bd_amoeba_2_slow_growth_time,   200
1186   },
1187   {
1188     EL_BDX_AMOEBA_2,                    -1,
1189     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1190     &li.bd_amoeba_2_content_too_big,    EL_BDX_ROCK
1191   },
1192   {
1193     EL_BDX_AMOEBA_2,                    -1,
1194     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
1195     &li.bd_amoeba_2_content_enclosed,   EL_BDX_DIAMOND
1196   },
1197   {
1198     EL_BDX_AMOEBA_2,                    -1,
1199     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1200     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1201   },
1202   {
1203     EL_BDX_AMOEBA_2,                    -1,
1204     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1205     &li.bd_amoeba_2_content_looks_like, EL_BDX_AMOEBA_2
1206   },
1207   {
1208     EL_BDX_AMOEBA_2,                    -1,
1209     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1210     &li.bd_amoeba_2_slow_growth_rate,   3
1211   },
1212   {
1213     EL_BDX_AMOEBA_2,                    -1,
1214     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1215     &li.bd_amoeba_2_fast_growth_rate,   25
1216   },
1217   {
1218     EL_BDX_AMOEBA_2,                    -1,
1219     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1220     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1221   },
1222
1223   {
1224     EL_YAMYAM,                          -1,
1225     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1226     &li.yamyam_content,                 EL_ROCK, NULL,
1227     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1228   },
1229   {
1230     EL_YAMYAM,                          -1,
1231     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1232     &li.score[SC_YAMYAM],               10
1233   },
1234
1235   {
1236     EL_ROBOT,                           -1,
1237     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1238     &li.score[SC_ROBOT],                10
1239   },
1240   {
1241     EL_ROBOT,                           -1,
1242     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1243     &li.slurp_score,                    10
1244   },
1245
1246   {
1247     EL_ROBOT_WHEEL,                     -1,
1248     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1249     &li.time_wheel,                     10
1250   },
1251
1252   {
1253     EL_MAGIC_WALL,                      -1,
1254     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1255     &li.time_magic_wall,                10
1256   },
1257
1258   {
1259     EL_GAME_OF_LIFE,                    -1,
1260     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1261     &li.game_of_life[0],                2
1262   },
1263   {
1264     EL_GAME_OF_LIFE,                    -1,
1265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1266     &li.game_of_life[1],                3
1267   },
1268   {
1269     EL_GAME_OF_LIFE,                    -1,
1270     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1271     &li.game_of_life[2],                3
1272   },
1273   {
1274     EL_GAME_OF_LIFE,                    -1,
1275     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1276     &li.game_of_life[3],                3
1277   },
1278   {
1279     EL_GAME_OF_LIFE,                    -1,
1280     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1281     &li.use_life_bugs,                  FALSE
1282   },
1283
1284   {
1285     EL_BIOMAZE,                         -1,
1286     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1287     &li.biomaze[0],                     2
1288   },
1289   {
1290     EL_BIOMAZE,                         -1,
1291     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1292     &li.biomaze[1],                     3
1293   },
1294   {
1295     EL_BIOMAZE,                         -1,
1296     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1297     &li.biomaze[2],                     3
1298   },
1299   {
1300     EL_BIOMAZE,                         -1,
1301     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1302     &li.biomaze[3],                     3
1303   },
1304
1305   {
1306     EL_TIMEGATE_SWITCH,                 -1,
1307     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1308     &li.time_timegate,                  10
1309   },
1310
1311   {
1312     EL_LIGHT_SWITCH_ACTIVE,             -1,
1313     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1314     &li.time_light,                     10
1315   },
1316
1317   {
1318     EL_SHIELD_NORMAL,                   -1,
1319     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1320     &li.shield_normal_time,             10
1321   },
1322   {
1323     EL_SHIELD_NORMAL,                   -1,
1324     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1325     &li.score[SC_SHIELD],               10
1326   },
1327
1328   {
1329     EL_SHIELD_DEADLY,                   -1,
1330     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1331     &li.shield_deadly_time,             10
1332   },
1333   {
1334     EL_SHIELD_DEADLY,                   -1,
1335     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1336     &li.score[SC_SHIELD],               10
1337   },
1338
1339   {
1340     EL_EXTRA_TIME,                      -1,
1341     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1342     &li.extra_time,                     10
1343   },
1344   {
1345     EL_EXTRA_TIME,                      -1,
1346     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1347     &li.extra_time_score,               10
1348   },
1349
1350   {
1351     EL_TIME_ORB_FULL,                   -1,
1352     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1353     &li.time_orb_time,                  10
1354   },
1355   {
1356     EL_TIME_ORB_FULL,                   -1,
1357     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1358     &li.use_time_orb_bug,               FALSE
1359   },
1360
1361   {
1362     EL_SPRING,                          -1,
1363     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1364     &li.use_spring_bug,                 FALSE
1365   },
1366
1367   {
1368     EL_EMC_ANDROID,                     -1,
1369     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1370     &li.android_move_time,              10
1371   },
1372   {
1373     EL_EMC_ANDROID,                     -1,
1374     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1375     &li.android_clone_time,             10
1376   },
1377   {
1378     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1379     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1380     &li.android_clone_element[0],       EL_EMPTY, NULL,
1381     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1382   },
1383   {
1384     EL_EMC_ANDROID,                     -1,
1385     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1386     &li.android_clone_element[0],       EL_EMPTY, NULL,
1387     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1388   },
1389
1390   {
1391     EL_EMC_LENSES,                      -1,
1392     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1393     &li.lenses_score,                   10
1394   },
1395   {
1396     EL_EMC_LENSES,                      -1,
1397     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1398     &li.lenses_time,                    10
1399   },
1400
1401   {
1402     EL_EMC_MAGNIFIER,                   -1,
1403     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1404     &li.magnify_score,                  10
1405   },
1406   {
1407     EL_EMC_MAGNIFIER,                   -1,
1408     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1409     &li.magnify_time,                   10
1410   },
1411
1412   {
1413     EL_EMC_MAGIC_BALL,                  -1,
1414     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1415     &li.ball_time,                      10
1416   },
1417   {
1418     EL_EMC_MAGIC_BALL,                  -1,
1419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1420     &li.ball_random,                    FALSE
1421   },
1422   {
1423     EL_EMC_MAGIC_BALL,                  -1,
1424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1425     &li.ball_active_initial,            FALSE
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL,                  -1,
1429     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1430     &li.ball_content,                   EL_EMPTY, NULL,
1431     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1432   },
1433
1434   {
1435     EL_SOKOBAN_FIELD_EMPTY,             -1,
1436     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1437     &li.sb_fields_needed,               TRUE
1438   },
1439
1440   {
1441     EL_SOKOBAN_OBJECT,                  -1,
1442     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1443     &li.sb_objects_needed,              TRUE
1444   },
1445
1446   {
1447     EL_MM_MCDUFFIN,                     -1,
1448     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1449     &li.mm_laser_red,                   FALSE
1450   },
1451   {
1452     EL_MM_MCDUFFIN,                     -1,
1453     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1454     &li.mm_laser_green,                 FALSE
1455   },
1456   {
1457     EL_MM_MCDUFFIN,                     -1,
1458     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1459     &li.mm_laser_blue,                  TRUE
1460   },
1461
1462   {
1463     EL_DF_LASER,                        -1,
1464     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1465     &li.df_laser_red,                   TRUE
1466   },
1467   {
1468     EL_DF_LASER,                        -1,
1469     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1470     &li.df_laser_green,                 TRUE
1471   },
1472   {
1473     EL_DF_LASER,                        -1,
1474     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1475     &li.df_laser_blue,                  FALSE
1476   },
1477
1478   {
1479     EL_MM_FUSE_ACTIVE,                  -1,
1480     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1481     &li.mm_time_fuse,                   25
1482   },
1483   {
1484     EL_MM_BOMB,                         -1,
1485     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1486     &li.mm_time_bomb,                   75
1487   },
1488
1489   {
1490     EL_MM_GRAY_BALL,                    -1,
1491     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1492     &li.mm_time_ball,                   75
1493   },
1494   {
1495     EL_MM_GRAY_BALL,                    -1,
1496     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1497     &li.mm_ball_choice_mode,            ANIM_RANDOM
1498   },
1499   {
1500     EL_MM_GRAY_BALL,                    -1,
1501     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1502     &li.mm_ball_content,                EL_EMPTY, NULL,
1503     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1504   },
1505   {
1506     EL_MM_GRAY_BALL,                    -1,
1507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1508     &li.rotate_mm_ball_content,         TRUE
1509   },
1510   {
1511     EL_MM_GRAY_BALL,                    -1,
1512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1513     &li.explode_mm_ball,                FALSE
1514   },
1515
1516   {
1517     EL_MM_STEEL_BLOCK,                  -1,
1518     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1519     &li.mm_time_block,                  75
1520   },
1521   {
1522     EL_MM_LIGHTBALL,                    -1,
1523     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1524     &li.score[SC_ELEM_BONUS],           10
1525   },
1526
1527   {
1528     -1,                                 -1,
1529     -1,                                 -1,
1530     NULL,                               -1
1531   }
1532 };
1533
1534 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1535 {
1536   {
1537     -1,                                 -1,
1538     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1539     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1540   },
1541   {
1542     -1,                                 -1,
1543     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1544     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1545   },
1546
1547   {
1548     -1,                                 -1,
1549     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1550     &xx_envelope.autowrap,              FALSE
1551   },
1552   {
1553     -1,                                 -1,
1554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1555     &xx_envelope.centered,              FALSE
1556   },
1557
1558   {
1559     -1,                                 -1,
1560     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1561     &xx_envelope.text,                  -1, NULL,
1562     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1563     &xx_default_string_empty[0]
1564   },
1565
1566   {
1567     -1,                                 -1,
1568     -1,                                 -1,
1569     NULL,                               -1
1570   }
1571 };
1572
1573 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1574 {
1575   {
1576     -1,                                 -1,
1577     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1578     &xx_ei.description[0],              -1,
1579     &yy_ei.description[0],
1580     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1581     &xx_default_description[0]
1582   },
1583
1584   {
1585     -1,                                 -1,
1586     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1587     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1588     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1589   },
1590 #if ENABLE_RESERVED_CODE
1591   // (reserved for later use)
1592   {
1593     -1,                                 -1,
1594     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1595     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1596     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1597   },
1598 #endif
1599
1600   {
1601     -1,                                 -1,
1602     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1603     &xx_ei.use_gfx_element,             FALSE,
1604     &yy_ei.use_gfx_element
1605   },
1606   {
1607     -1,                                 -1,
1608     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1609     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1610     &yy_ei.gfx_element_initial
1611   },
1612
1613   {
1614     -1,                                 -1,
1615     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1616     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1617     &yy_ei.access_direction
1618   },
1619
1620   {
1621     -1,                                 -1,
1622     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1623     &xx_ei.collect_score_initial,       10,
1624     &yy_ei.collect_score_initial
1625   },
1626   {
1627     -1,                                 -1,
1628     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1629     &xx_ei.collect_count_initial,       1,
1630     &yy_ei.collect_count_initial
1631   },
1632
1633   {
1634     -1,                                 -1,
1635     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1636     &xx_ei.ce_value_fixed_initial,      0,
1637     &yy_ei.ce_value_fixed_initial
1638   },
1639   {
1640     -1,                                 -1,
1641     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1642     &xx_ei.ce_value_random_initial,     0,
1643     &yy_ei.ce_value_random_initial
1644   },
1645   {
1646     -1,                                 -1,
1647     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1648     &xx_ei.use_last_ce_value,           FALSE,
1649     &yy_ei.use_last_ce_value
1650   },
1651
1652   {
1653     -1,                                 -1,
1654     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1655     &xx_ei.push_delay_fixed,            8,
1656     &yy_ei.push_delay_fixed
1657   },
1658   {
1659     -1,                                 -1,
1660     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1661     &xx_ei.push_delay_random,           8,
1662     &yy_ei.push_delay_random
1663   },
1664   {
1665     -1,                                 -1,
1666     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1667     &xx_ei.drop_delay_fixed,            0,
1668     &yy_ei.drop_delay_fixed
1669   },
1670   {
1671     -1,                                 -1,
1672     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1673     &xx_ei.drop_delay_random,           0,
1674     &yy_ei.drop_delay_random
1675   },
1676   {
1677     -1,                                 -1,
1678     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1679     &xx_ei.move_delay_fixed,            0,
1680     &yy_ei.move_delay_fixed
1681   },
1682   {
1683     -1,                                 -1,
1684     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1685     &xx_ei.move_delay_random,           0,
1686     &yy_ei.move_delay_random
1687   },
1688   {
1689     -1,                                 -1,
1690     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1691     &xx_ei.step_delay_fixed,            0,
1692     &yy_ei.step_delay_fixed
1693   },
1694   {
1695     -1,                                 -1,
1696     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1697     &xx_ei.step_delay_random,           0,
1698     &yy_ei.step_delay_random
1699   },
1700
1701   {
1702     -1,                                 -1,
1703     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1704     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1705     &yy_ei.move_pattern
1706   },
1707   {
1708     -1,                                 -1,
1709     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1710     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1711     &yy_ei.move_direction_initial
1712   },
1713   {
1714     -1,                                 -1,
1715     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1716     &xx_ei.move_stepsize,               TILEX / 8,
1717     &yy_ei.move_stepsize
1718   },
1719
1720   {
1721     -1,                                 -1,
1722     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1723     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1724     &yy_ei.move_enter_element
1725   },
1726   {
1727     -1,                                 -1,
1728     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1729     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1730     &yy_ei.move_leave_element
1731   },
1732   {
1733     -1,                                 -1,
1734     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1735     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1736     &yy_ei.move_leave_type
1737   },
1738
1739   {
1740     -1,                                 -1,
1741     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1742     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1743     &yy_ei.slippery_type
1744   },
1745
1746   {
1747     -1,                                 -1,
1748     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1749     &xx_ei.explosion_type,              EXPLODES_3X3,
1750     &yy_ei.explosion_type
1751   },
1752   {
1753     -1,                                 -1,
1754     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1755     &xx_ei.explosion_delay,             16,
1756     &yy_ei.explosion_delay
1757   },
1758   {
1759     -1,                                 -1,
1760     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1761     &xx_ei.ignition_delay,              8,
1762     &yy_ei.ignition_delay
1763   },
1764
1765   {
1766     -1,                                 -1,
1767     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1768     &xx_ei.content,                     EL_EMPTY_SPACE,
1769     &yy_ei.content,
1770     &xx_num_contents,                   1, 1
1771   },
1772
1773   // ---------- "num_change_pages" must be the last entry ---------------------
1774
1775   {
1776     -1,                                 SAVE_CONF_ALWAYS,
1777     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1778     &xx_ei.num_change_pages,            1,
1779     &yy_ei.num_change_pages
1780   },
1781
1782   {
1783     -1,                                 -1,
1784     -1,                                 -1,
1785     NULL,                               -1,
1786     NULL
1787   }
1788 };
1789
1790 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1791 {
1792   // ---------- "current_change_page" must be the first entry -----------------
1793
1794   {
1795     -1,                                 SAVE_CONF_ALWAYS,
1796     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1797     &xx_current_change_page,            -1
1798   },
1799
1800   // ---------- (the remaining entries can be in any order) -------------------
1801
1802   {
1803     -1,                                 -1,
1804     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1805     &xx_change.can_change,              FALSE
1806   },
1807
1808   {
1809     -1,                                 -1,
1810     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1811     &xx_event_bits[0],                  0
1812   },
1813   {
1814     -1,                                 -1,
1815     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1816     &xx_event_bits[1],                  0
1817   },
1818
1819   {
1820     -1,                                 -1,
1821     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1822     &xx_change.trigger_player,          CH_PLAYER_ANY
1823   },
1824   {
1825     -1,                                 -1,
1826     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1827     &xx_change.trigger_side,            CH_SIDE_ANY
1828   },
1829   {
1830     -1,                                 -1,
1831     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1832     &xx_change.trigger_page,            CH_PAGE_ANY
1833   },
1834
1835   {
1836     -1,                                 -1,
1837     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1838     &xx_change.target_element,          EL_EMPTY_SPACE
1839   },
1840
1841   {
1842     -1,                                 -1,
1843     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1844     &xx_change.delay_fixed,             0
1845   },
1846   {
1847     -1,                                 -1,
1848     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1849     &xx_change.delay_random,            0
1850   },
1851   {
1852     -1,                                 -1,
1853     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1854     &xx_change.delay_frames,            FRAMES_PER_SECOND
1855   },
1856
1857   {
1858     -1,                                 -1,
1859     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1860     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1861   },
1862
1863   {
1864     -1,                                 -1,
1865     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1866     &xx_change.explode,                 FALSE
1867   },
1868   {
1869     -1,                                 -1,
1870     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1871     &xx_change.use_target_content,      FALSE
1872   },
1873   {
1874     -1,                                 -1,
1875     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1876     &xx_change.only_if_complete,        FALSE
1877   },
1878   {
1879     -1,                                 -1,
1880     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1881     &xx_change.use_random_replace,      FALSE
1882   },
1883   {
1884     -1,                                 -1,
1885     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1886     &xx_change.random_percentage,       100
1887   },
1888   {
1889     -1,                                 -1,
1890     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1891     &xx_change.replace_when,            CP_WHEN_EMPTY
1892   },
1893
1894   {
1895     -1,                                 -1,
1896     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1897     &xx_change.has_action,              FALSE
1898   },
1899   {
1900     -1,                                 -1,
1901     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1902     &xx_change.action_type,             CA_NO_ACTION
1903   },
1904   {
1905     -1,                                 -1,
1906     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1907     &xx_change.action_mode,             CA_MODE_UNDEFINED
1908   },
1909   {
1910     -1,                                 -1,
1911     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1912     &xx_change.action_arg,              CA_ARG_UNDEFINED
1913   },
1914
1915   {
1916     -1,                                 -1,
1917     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1918     &xx_change.action_element,          EL_EMPTY_SPACE
1919   },
1920
1921   {
1922     -1,                                 -1,
1923     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1924     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1925     &xx_num_contents,                   1, 1
1926   },
1927
1928   {
1929     -1,                                 -1,
1930     -1,                                 -1,
1931     NULL,                               -1
1932   }
1933 };
1934
1935 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1936 {
1937   {
1938     -1,                                 -1,
1939     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1940     &xx_ei.description[0],              -1, NULL,
1941     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1942     &xx_default_description[0]
1943   },
1944
1945   {
1946     -1,                                 -1,
1947     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1948     &xx_ei.use_gfx_element,             FALSE
1949   },
1950   {
1951     -1,                                 -1,
1952     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1953     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1954   },
1955
1956   {
1957     -1,                                 -1,
1958     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1959     &xx_group.choice_mode,              ANIM_RANDOM
1960   },
1961
1962   {
1963     -1,                                 -1,
1964     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1965     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1966     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1967   },
1968
1969   {
1970     -1,                                 -1,
1971     -1,                                 -1,
1972     NULL,                               -1
1973   }
1974 };
1975
1976 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1977 {
1978   {
1979     -1,                                 -1,
1980     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1981     &xx_ei.use_gfx_element,             FALSE
1982   },
1983   {
1984     -1,                                 -1,
1985     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1986     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1987   },
1988
1989   {
1990     -1,                                 -1,
1991     -1,                                 -1,
1992     NULL,                               -1
1993   }
1994 };
1995
1996 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1997 {
1998   {
1999     EL_PLAYER_1,                        -1,
2000     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
2001     &li.block_snap_field,               TRUE
2002   },
2003   {
2004     EL_PLAYER_1,                        -1,
2005     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
2006     &li.continuous_snapping,            TRUE
2007   },
2008   {
2009     EL_PLAYER_1,                        -1,
2010     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
2011     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
2012   },
2013   {
2014     EL_PLAYER_1,                        -1,
2015     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
2016     &li.use_start_element[0],           FALSE
2017   },
2018   {
2019     EL_PLAYER_1,                        -1,
2020     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
2021     &li.start_element[0],               EL_PLAYER_1
2022   },
2023   {
2024     EL_PLAYER_1,                        -1,
2025     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
2026     &li.use_artwork_element[0],         FALSE
2027   },
2028   {
2029     EL_PLAYER_1,                        -1,
2030     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
2031     &li.artwork_element[0],             EL_PLAYER_1
2032   },
2033   {
2034     EL_PLAYER_1,                        -1,
2035     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
2036     &li.use_explosion_element[0],       FALSE
2037   },
2038   {
2039     EL_PLAYER_1,                        -1,
2040     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
2041     &li.explosion_element[0],           EL_PLAYER_1
2042   },
2043
2044   {
2045     -1,                                 -1,
2046     -1,                                 -1,
2047     NULL,                               -1
2048   }
2049 };
2050
2051 static struct
2052 {
2053   int filetype;
2054   char *id;
2055 }
2056 filetype_id_list[] =
2057 {
2058   { LEVEL_FILE_TYPE_RND,        "RND"   },
2059   { LEVEL_FILE_TYPE_BD,         "BD"    },
2060   { LEVEL_FILE_TYPE_EM,         "EM"    },
2061   { LEVEL_FILE_TYPE_SP,         "SP"    },
2062   { LEVEL_FILE_TYPE_DX,         "DX"    },
2063   { LEVEL_FILE_TYPE_SB,         "SB"    },
2064   { LEVEL_FILE_TYPE_DC,         "DC"    },
2065   { LEVEL_FILE_TYPE_MM,         "MM"    },
2066   { LEVEL_FILE_TYPE_MM,         "DF"    },
2067   { -1,                         NULL    },
2068 };
2069
2070
2071 // ============================================================================
2072 // level file functions
2073 // ============================================================================
2074
2075 static boolean check_special_flags(char *flag)
2076 {
2077   if (strEqual(options.special_flags, flag) ||
2078       strEqual(leveldir_current->special_flags, flag))
2079     return TRUE;
2080
2081   return FALSE;
2082 }
2083
2084 static struct DateInfo getCurrentDate(void)
2085 {
2086   time_t epoch_seconds = time(NULL);
2087   struct tm *now = localtime(&epoch_seconds);
2088   struct DateInfo date;
2089
2090   date.year  = now->tm_year + 1900;
2091   date.month = now->tm_mon  + 1;
2092   date.day   = now->tm_mday;
2093
2094   date.src   = DATE_SRC_CLOCK;
2095
2096   return date;
2097 }
2098
2099 static void resetEventFlags(struct ElementChangeInfo *change)
2100 {
2101   int i;
2102
2103   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2104     change->has_event[i] = FALSE;
2105 }
2106
2107 static void resetEventBits(void)
2108 {
2109   int i;
2110
2111   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2112     xx_event_bits[i] = 0;
2113 }
2114
2115 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2116 {
2117   int i;
2118
2119   /* important: only change event flag if corresponding event bit is set
2120      (this is because all xx_event_bits[] values are loaded separately,
2121      and all xx_event_bits[] values are set back to zero before loading
2122      another value xx_event_bits[x] (each value representing 32 flags)) */
2123
2124   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2125     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2126       change->has_event[i] = TRUE;
2127 }
2128
2129 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2130 {
2131   int i;
2132
2133   /* in contrast to the above function setEventFlagsFromEventBits(), it
2134      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2135      depending on the corresponding change->has_event[i] values here, as
2136      all xx_event_bits[] values are reset in resetEventBits() before */
2137
2138   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2139     if (change->has_event[i])
2140       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2141 }
2142
2143 static char *getDefaultElementDescription(struct ElementInfo *ei)
2144 {
2145   static char description[MAX_ELEMENT_NAME_LEN + 1];
2146   char *default_description = (ei->custom_description != NULL ?
2147                                ei->custom_description :
2148                                ei->editor_description);
2149   int i;
2150
2151   // always start with reliable default values
2152   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2153     description[i] = '\0';
2154
2155   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2156   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2157
2158   return &description[0];
2159 }
2160
2161 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2162 {
2163   char *default_description = getDefaultElementDescription(ei);
2164   int i;
2165
2166   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2167     ei->description[i] = default_description[i];
2168 }
2169
2170 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2171 {
2172   int i;
2173
2174   for (i = 0; conf[i].data_type != -1; i++)
2175   {
2176     int default_value = conf[i].default_value;
2177     int data_type = conf[i].data_type;
2178     int conf_type = conf[i].conf_type;
2179     int byte_mask = conf_type & CONF_MASK_BYTES;
2180
2181     if (byte_mask == CONF_MASK_MULTI_BYTES)
2182     {
2183       int default_num_entities = conf[i].default_num_entities;
2184       int max_num_entities = conf[i].max_num_entities;
2185
2186       *(int *)(conf[i].num_entities) = default_num_entities;
2187
2188       if (data_type == TYPE_STRING)
2189       {
2190         char *default_string = conf[i].default_string;
2191         char *string = (char *)(conf[i].value);
2192
2193         strncpy(string, default_string, max_num_entities);
2194       }
2195       else if (data_type == TYPE_ELEMENT_LIST)
2196       {
2197         int *element_array = (int *)(conf[i].value);
2198         int j;
2199
2200         for (j = 0; j < max_num_entities; j++)
2201           element_array[j] = default_value;
2202       }
2203       else if (data_type == TYPE_CONTENT_LIST)
2204       {
2205         struct Content *content = (struct Content *)(conf[i].value);
2206         int c, x, y;
2207
2208         for (c = 0; c < max_num_entities; c++)
2209           for (y = 0; y < 3; y++)
2210             for (x = 0; x < 3; x++)
2211               content[c].e[x][y] = default_value;
2212       }
2213     }
2214     else        // constant size configuration data (1, 2 or 4 bytes)
2215     {
2216       if (data_type == TYPE_BOOLEAN)
2217         *(boolean *)(conf[i].value) = default_value;
2218       else
2219         *(int *)    (conf[i].value) = default_value;
2220     }
2221   }
2222 }
2223
2224 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2225 {
2226   int i;
2227
2228   for (i = 0; conf[i].data_type != -1; i++)
2229   {
2230     int data_type = conf[i].data_type;
2231     int conf_type = conf[i].conf_type;
2232     int byte_mask = conf_type & CONF_MASK_BYTES;
2233
2234     if (byte_mask == CONF_MASK_MULTI_BYTES)
2235     {
2236       int max_num_entities = conf[i].max_num_entities;
2237
2238       if (data_type == TYPE_STRING)
2239       {
2240         char *string      = (char *)(conf[i].value);
2241         char *string_copy = (char *)(conf[i].value_copy);
2242
2243         strncpy(string_copy, string, max_num_entities);
2244       }
2245       else if (data_type == TYPE_ELEMENT_LIST)
2246       {
2247         int *element_array      = (int *)(conf[i].value);
2248         int *element_array_copy = (int *)(conf[i].value_copy);
2249         int j;
2250
2251         for (j = 0; j < max_num_entities; j++)
2252           element_array_copy[j] = element_array[j];
2253       }
2254       else if (data_type == TYPE_CONTENT_LIST)
2255       {
2256         struct Content *content      = (struct Content *)(conf[i].value);
2257         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2258         int c, x, y;
2259
2260         for (c = 0; c < max_num_entities; c++)
2261           for (y = 0; y < 3; y++)
2262             for (x = 0; x < 3; x++)
2263               content_copy[c].e[x][y] = content[c].e[x][y];
2264       }
2265     }
2266     else        // constant size configuration data (1, 2 or 4 bytes)
2267     {
2268       if (data_type == TYPE_BOOLEAN)
2269         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2270       else
2271         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2272     }
2273   }
2274 }
2275
2276 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2277 {
2278   int i;
2279
2280   xx_ei = *ei_from;     // copy element data into temporary buffer
2281   yy_ei = *ei_to;       // copy element data into temporary buffer
2282
2283   copyConfigFromConfigList(chunk_config_CUSX_base);
2284
2285   *ei_from = xx_ei;
2286   *ei_to   = yy_ei;
2287
2288   // ---------- reinitialize and copy change pages ----------
2289
2290   ei_to->num_change_pages = ei_from->num_change_pages;
2291   ei_to->current_change_page = ei_from->current_change_page;
2292
2293   setElementChangePages(ei_to, ei_to->num_change_pages);
2294
2295   for (i = 0; i < ei_to->num_change_pages; i++)
2296     ei_to->change_page[i] = ei_from->change_page[i];
2297
2298   // ---------- copy group element info ----------
2299   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2300     *ei_to->group = *ei_from->group;
2301
2302   // mark this custom element as modified
2303   ei_to->modified_settings = TRUE;
2304 }
2305
2306 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2307 {
2308   int change_page_size = sizeof(struct ElementChangeInfo);
2309
2310   ei->num_change_pages = MAX(1, change_pages);
2311
2312   ei->change_page =
2313     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2314
2315   if (ei->current_change_page >= ei->num_change_pages)
2316     ei->current_change_page = ei->num_change_pages - 1;
2317
2318   ei->change = &ei->change_page[ei->current_change_page];
2319 }
2320
2321 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2322 {
2323   xx_change = *change;          // copy change data into temporary buffer
2324
2325   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2326
2327   *change = xx_change;
2328
2329   resetEventFlags(change);
2330
2331   change->direct_action = 0;
2332   change->other_action = 0;
2333
2334   change->pre_change_function = NULL;
2335   change->change_function = NULL;
2336   change->post_change_function = NULL;
2337 }
2338
2339 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2340 {
2341   boolean add_border = FALSE;
2342   int x1 = 0;
2343   int y1 = 0;
2344   int x2 = STD_LEV_FIELDX - 1;
2345   int y2 = STD_LEV_FIELDY - 1;
2346   int i, x, y;
2347
2348   li = *level;          // copy level data into temporary buffer
2349   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2350   *level = li;          // copy temporary buffer back to level data
2351
2352   setLevelInfoToDefaults_BD();
2353   setLevelInfoToDefaults_EM();
2354   setLevelInfoToDefaults_SP();
2355   setLevelInfoToDefaults_MM();
2356
2357   level->native_bd_level = &native_bd_level;
2358   level->native_em_level = &native_em_level;
2359   level->native_sp_level = &native_sp_level;
2360   level->native_mm_level = &native_mm_level;
2361
2362   level->file_version = FILE_VERSION_ACTUAL;
2363   level->game_version = GAME_VERSION_ACTUAL;
2364
2365   level->creation_date = getCurrentDate();
2366
2367   level->encoding_16bit_field  = TRUE;
2368   level->encoding_16bit_yamyam = TRUE;
2369   level->encoding_16bit_amoeba = TRUE;
2370
2371   // clear level name and level author string buffers
2372   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2373     level->name[i] = '\0';
2374   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2375     level->author[i] = '\0';
2376
2377   // set level name and level author to default values
2378   strcpy(level->name, NAMELESS_LEVEL_NAME);
2379   strcpy(level->author, ANONYMOUS_NAME);
2380
2381   // set default game engine type
2382   level->game_engine_type = setup.default_game_engine_type;
2383
2384   // some game engines should have a default playfield with border elements
2385   if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2386       level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2387       level->game_engine_type == GAME_ENGINE_TYPE_SP)
2388   {
2389     add_border = TRUE;
2390     x1++;
2391     y1++;
2392     x2--;
2393     y2--;
2394   }
2395
2396   // set level playfield to playable default level with player and exit
2397   for (x = 0; x < MAX_LEV_FIELDX; x++)
2398   {
2399     for (y = 0; y < MAX_LEV_FIELDY; y++)
2400     {
2401       if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2402                          y == 0 || y == STD_LEV_FIELDY - 1))
2403         level->field[x][y] = getEngineElement(EL_STEELWALL);
2404       else
2405         level->field[x][y] = getEngineElement(EL_SAND);
2406     }
2407   }
2408
2409   level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2410   level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2411
2412   BorderElement = getEngineElement(EL_STEELWALL);
2413
2414   // detect custom elements when loading them
2415   level->file_has_custom_elements = FALSE;
2416
2417   // set random colors for BD style levels according to preferred color type
2418   SetRandomLevelColors_BD(setup.bd_default_color_type);
2419
2420   // set default color type and colors for BD style level colors
2421   SetDefaultLevelColorType_BD();
2422   SetDefaultLevelColors_BD();
2423
2424   // set all bug compatibility flags to "false" => do not emulate this bug
2425   level->use_action_after_change_bug = FALSE;
2426
2427   if (leveldir_current)
2428   {
2429     // try to determine better author name than 'anonymous'
2430     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2431     {
2432       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2433       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2434     }
2435     else
2436     {
2437       switch (LEVELCLASS(leveldir_current))
2438       {
2439         case LEVELCLASS_TUTORIAL:
2440           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2441           break;
2442
2443         case LEVELCLASS_CONTRIB:
2444           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2445           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2446           break;
2447
2448         case LEVELCLASS_PRIVATE:
2449           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2450           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2451           break;
2452
2453         default:
2454           // keep default value
2455           break;
2456       }
2457     }
2458   }
2459 }
2460
2461 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2462 {
2463   static boolean clipboard_elements_initialized = FALSE;
2464   int i;
2465
2466   InitElementPropertiesStatic();
2467
2468   li = *level;          // copy level data into temporary buffer
2469   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2470   *level = li;          // copy temporary buffer back to level data
2471
2472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2473   {
2474     int element = i;
2475     struct ElementInfo *ei = &element_info[element];
2476
2477     if (element == EL_MM_GRAY_BALL)
2478     {
2479       struct LevelInfo_MM *level_mm = level->native_mm_level;
2480       int j;
2481
2482       for (j = 0; j < level->num_mm_ball_contents; j++)
2483         level->mm_ball_content[j] =
2484           map_element_MM_to_RND(level_mm->ball_content[j]);
2485     }
2486
2487     // never initialize clipboard elements after the very first time
2488     // (to be able to use clipboard elements between several levels)
2489     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2490       continue;
2491
2492     if (IS_ENVELOPE(element))
2493     {
2494       int envelope_nr = element - EL_ENVELOPE_1;
2495
2496       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2497
2498       level->envelope[envelope_nr] = xx_envelope;
2499     }
2500
2501     if (IS_CUSTOM_ELEMENT(element) ||
2502         IS_GROUP_ELEMENT(element) ||
2503         IS_INTERNAL_ELEMENT(element))
2504     {
2505       xx_ei = *ei;      // copy element data into temporary buffer
2506
2507       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2508
2509       *ei = xx_ei;
2510     }
2511
2512     setElementChangePages(ei, 1);
2513     setElementChangeInfoToDefaults(ei->change);
2514
2515     if (IS_CUSTOM_ELEMENT(element) ||
2516         IS_GROUP_ELEMENT(element))
2517     {
2518       setElementDescriptionToDefault(ei);
2519
2520       ei->modified_settings = FALSE;
2521     }
2522
2523     if (IS_CUSTOM_ELEMENT(element) ||
2524         IS_INTERNAL_ELEMENT(element))
2525     {
2526       // internal values used in level editor
2527
2528       ei->access_type = 0;
2529       ei->access_layer = 0;
2530       ei->access_protected = 0;
2531       ei->walk_to_action = 0;
2532       ei->smash_targets = 0;
2533       ei->deadliness = 0;
2534
2535       ei->can_explode_by_fire = FALSE;
2536       ei->can_explode_smashed = FALSE;
2537       ei->can_explode_impact = FALSE;
2538
2539       ei->current_change_page = 0;
2540     }
2541
2542     if (IS_GROUP_ELEMENT(element) ||
2543         IS_INTERNAL_ELEMENT(element))
2544     {
2545       struct ElementGroupInfo *group;
2546
2547       // initialize memory for list of elements in group
2548       if (ei->group == NULL)
2549         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2550
2551       group = ei->group;
2552
2553       xx_group = *group;        // copy group data into temporary buffer
2554
2555       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2556
2557       *group = xx_group;
2558     }
2559
2560     if (IS_EMPTY_ELEMENT(element) ||
2561         IS_INTERNAL_ELEMENT(element))
2562     {
2563       xx_ei = *ei;              // copy element data into temporary buffer
2564
2565       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2566
2567       *ei = xx_ei;
2568     }
2569   }
2570
2571   clipboard_elements_initialized = TRUE;
2572 }
2573
2574 static void setLevelInfoToDefaults(struct LevelInfo *level,
2575                                    boolean level_info_only,
2576                                    boolean reset_file_status)
2577 {
2578   setLevelInfoToDefaults_Level(level);
2579
2580   if (!level_info_only)
2581     setLevelInfoToDefaults_Elements(level);
2582
2583   if (reset_file_status)
2584   {
2585     level->no_valid_file = FALSE;
2586     level->no_level_file = FALSE;
2587   }
2588
2589   level->changed = FALSE;
2590 }
2591
2592 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2593 {
2594   level_file_info->nr = 0;
2595   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2596   level_file_info->packed = FALSE;
2597
2598   setString(&level_file_info->basename, NULL);
2599   setString(&level_file_info->filename, NULL);
2600 }
2601
2602 int getMappedElement_SB(int, boolean);
2603
2604 static void ActivateLevelTemplate(void)
2605 {
2606   int x, y;
2607
2608   if (check_special_flags("load_xsb_to_ces"))
2609   {
2610     // fill smaller playfields with padding "beyond border wall" elements
2611     if (level.fieldx < level_template.fieldx ||
2612         level.fieldy < level_template.fieldy)
2613     {
2614       short field[level.fieldx][level.fieldy];
2615       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2616       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2617       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2618       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2619
2620       // copy old playfield (which is smaller than the visible area)
2621       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2622         field[x][y] = level.field[x][y];
2623
2624       // fill new, larger playfield with "beyond border wall" elements
2625       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2626         level.field[x][y] = getMappedElement_SB('_', TRUE);
2627
2628       // copy the old playfield to the middle of the new playfield
2629       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2630         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2631
2632       level.fieldx = new_fieldx;
2633       level.fieldy = new_fieldy;
2634     }
2635   }
2636
2637   // Currently there is no special action needed to activate the template
2638   // data, because 'element_info' property settings overwrite the original
2639   // level data, while all other variables do not change.
2640
2641   // Exception: 'from_level_template' elements in the original level playfield
2642   // are overwritten with the corresponding elements at the same position in
2643   // playfield from the level template.
2644
2645   for (x = 0; x < level.fieldx; x++)
2646     for (y = 0; y < level.fieldy; y++)
2647       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2648         level.field[x][y] = level_template.field[x][y];
2649
2650   if (check_special_flags("load_xsb_to_ces"))
2651   {
2652     struct LevelInfo level_backup = level;
2653
2654     // overwrite all individual level settings from template level settings
2655     level = level_template;
2656
2657     // restore level file info
2658     level.file_info = level_backup.file_info;
2659
2660     // restore playfield size
2661     level.fieldx = level_backup.fieldx;
2662     level.fieldy = level_backup.fieldy;
2663
2664     // restore playfield content
2665     for (x = 0; x < level.fieldx; x++)
2666       for (y = 0; y < level.fieldy; y++)
2667         level.field[x][y] = level_backup.field[x][y];
2668
2669     // restore name and author from individual level
2670     strcpy(level.name,   level_backup.name);
2671     strcpy(level.author, level_backup.author);
2672
2673     // restore flag "use_custom_template"
2674     level.use_custom_template = level_backup.use_custom_template;
2675   }
2676 }
2677
2678 boolean isLevelsetFilename_BD(char *filename)
2679 {
2680   return (strSuffixLower(filename, ".bd") ||
2681           strSuffixLower(filename, ".bdr") ||
2682           strSuffixLower(filename, ".brc") ||
2683           strSuffixLower(filename, ".gds"));
2684 }
2685
2686 static boolean checkForPackageFromBasename_BD(char *basename)
2687 {
2688   // check for native BD level file extensions
2689   if (!isLevelsetFilename_BD(basename))
2690     return FALSE;
2691
2692   // check for standard single-level BD files (like "001.bd")
2693   if (strSuffixLower(basename, ".bd") &&
2694       strlen(basename) == 6 &&
2695       basename[0] >= '0' && basename[0] <= '9' &&
2696       basename[1] >= '0' && basename[1] <= '9' &&
2697       basename[2] >= '0' && basename[2] <= '9')
2698     return FALSE;
2699
2700   // this is a level package in native BD file format
2701   return TRUE;
2702 }
2703
2704 static char *getLevelFilenameFromBasename(char *basename)
2705 {
2706   static char *filename = NULL;
2707
2708   checked_free(filename);
2709
2710   filename = getPath2(getCurrentLevelDir(), basename);
2711
2712   return filename;
2713 }
2714
2715 static int getFileTypeFromBasename(char *basename)
2716 {
2717   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2718
2719   static char *filename = NULL;
2720   struct stat file_status;
2721
2722   // ---------- try to determine file type from filename ----------
2723
2724   // check for typical filename of a Supaplex level package file
2725   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2726     return LEVEL_FILE_TYPE_SP;
2727
2728   // check for typical filename of a Diamond Caves II level package file
2729   if (strSuffixLower(basename, ".dc") ||
2730       strSuffixLower(basename, ".dc2"))
2731     return LEVEL_FILE_TYPE_DC;
2732
2733   // check for typical filename of a Sokoban level package file
2734   if (strSuffixLower(basename, ".xsb") &&
2735       strchr(basename, '%') == NULL)
2736     return LEVEL_FILE_TYPE_SB;
2737
2738   // check for typical filename of a Boulder Dash (GDash) level package file
2739   if (checkForPackageFromBasename_BD(basename))
2740     return LEVEL_FILE_TYPE_BD;
2741
2742   // ---------- try to determine file type from filesize ----------
2743
2744   checked_free(filename);
2745   filename = getPath2(getCurrentLevelDir(), basename);
2746
2747   if (stat(filename, &file_status) == 0)
2748   {
2749     // check for typical filesize of a Supaplex level package file
2750     if (file_status.st_size == 170496)
2751       return LEVEL_FILE_TYPE_SP;
2752   }
2753
2754   return LEVEL_FILE_TYPE_UNKNOWN;
2755 }
2756
2757 static int getFileTypeFromMagicBytes(char *filename, int type)
2758 {
2759   File *file;
2760
2761   if ((file = openFile(filename, MODE_READ)))
2762   {
2763     char chunk_name[CHUNK_ID_LEN + 1];
2764
2765     getFileChunkBE(file, chunk_name, NULL);
2766
2767     if (strEqual(chunk_name, "MMII") ||
2768         strEqual(chunk_name, "MIRR"))
2769       type = LEVEL_FILE_TYPE_MM;
2770
2771     closeFile(file);
2772   }
2773
2774   return type;
2775 }
2776
2777 static boolean checkForPackageFromBasename(char *basename)
2778 {
2779   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2780   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2781
2782   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2783 }
2784
2785 static char *getSingleLevelBasenameExt(int nr, char *extension)
2786 {
2787   static char basename[MAX_FILENAME_LEN];
2788
2789   if (nr < 0)
2790     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2791   else
2792     sprintf(basename, "%03d.%s", nr, extension);
2793
2794   return basename;
2795 }
2796
2797 static char *getSingleLevelBasename(int nr)
2798 {
2799   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2800 }
2801
2802 static char *getPackedLevelBasename(int type)
2803 {
2804   static char basename[MAX_FILENAME_LEN];
2805   char *directory = getCurrentLevelDir();
2806   Directory *dir;
2807   DirectoryEntry *dir_entry;
2808
2809   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2810
2811   if ((dir = openDirectory(directory)) == NULL)
2812   {
2813     Warn("cannot read current level directory '%s'", directory);
2814
2815     return basename;
2816   }
2817
2818   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2819   {
2820     char *entry_basename = dir_entry->basename;
2821     int entry_type = getFileTypeFromBasename(entry_basename);
2822
2823     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2824     {
2825       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2826           type == entry_type)
2827       {
2828         strcpy(basename, entry_basename);
2829
2830         break;
2831       }
2832     }
2833   }
2834
2835   closeDirectory(dir);
2836
2837   return basename;
2838 }
2839
2840 static char *getSingleLevelFilename(int nr)
2841 {
2842   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2843 }
2844
2845 #if ENABLE_UNUSED_CODE
2846 static char *getPackedLevelFilename(int type)
2847 {
2848   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2849 }
2850 #endif
2851
2852 char *getDefaultLevelFilename(int nr)
2853 {
2854   return getSingleLevelFilename(nr);
2855 }
2856
2857 #if ENABLE_UNUSED_CODE
2858 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2859                                                  int type)
2860 {
2861   lfi->type = type;
2862   lfi->packed = FALSE;
2863
2864   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2865   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2866 }
2867 #endif
2868
2869 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2870                                                  int type, char *format, ...)
2871 {
2872   static char basename[MAX_FILENAME_LEN];
2873   va_list ap;
2874
2875   va_start(ap, format);
2876   vsprintf(basename, format, ap);
2877   va_end(ap);
2878
2879   lfi->type = type;
2880   lfi->packed = FALSE;
2881
2882   setString(&lfi->basename, basename);
2883   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2884 }
2885
2886 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2887                                                  int type)
2888 {
2889   lfi->type = type;
2890   lfi->packed = TRUE;
2891
2892   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2893   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2894 }
2895
2896 static int getFiletypeFromID(char *filetype_id)
2897 {
2898   char *filetype_id_lower;
2899   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2900   int i;
2901
2902   if (filetype_id == NULL)
2903     return LEVEL_FILE_TYPE_UNKNOWN;
2904
2905   filetype_id_lower = getStringToLower(filetype_id);
2906
2907   for (i = 0; filetype_id_list[i].id != NULL; i++)
2908   {
2909     char *id_lower = getStringToLower(filetype_id_list[i].id);
2910     
2911     if (strEqual(filetype_id_lower, id_lower))
2912       filetype = filetype_id_list[i].filetype;
2913
2914     free(id_lower);
2915
2916     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2917       break;
2918   }
2919
2920   free(filetype_id_lower);
2921
2922   return filetype;
2923 }
2924
2925 char *getLocalLevelTemplateFilename(void)
2926 {
2927   return getDefaultLevelFilename(-1);
2928 }
2929
2930 char *getGlobalLevelTemplateFilename(void)
2931 {
2932   // global variable "leveldir_current" must be modified in the loop below
2933   LevelDirTree *leveldir_current_last = leveldir_current;
2934   char *filename = NULL;
2935
2936   // check for template level in path from current to topmost tree node
2937
2938   while (leveldir_current != NULL)
2939   {
2940     filename = getDefaultLevelFilename(-1);
2941
2942     if (fileExists(filename))
2943       break;
2944
2945     leveldir_current = leveldir_current->node_parent;
2946   }
2947
2948   // restore global variable "leveldir_current" modified in above loop
2949   leveldir_current = leveldir_current_last;
2950
2951   return filename;
2952 }
2953
2954 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2955 {
2956   int nr = lfi->nr;
2957
2958   // special case: level number is negative => check for level template file
2959   if (nr < 0)
2960   {
2961     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2962                                          getSingleLevelBasename(-1));
2963
2964     // replace local level template filename with global template filename
2965     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2966
2967     // no fallback if template file not existing
2968     return;
2969   }
2970
2971   // special case: check for file name/pattern specified in "levelinfo.conf"
2972   if (leveldir_current->level_filename != NULL)
2973   {
2974     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2975
2976     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2977                                          leveldir_current->level_filename, nr);
2978
2979     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2980
2981     if (fileExists(lfi->filename))
2982       return;
2983   }
2984   else if (leveldir_current->level_filetype != NULL)
2985   {
2986     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2987
2988     // check for specified native level file with standard file name
2989     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2990                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2991     if (fileExists(lfi->filename))
2992       return;
2993   }
2994
2995   // check for native Rocks'n'Diamonds level file
2996   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2997                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2998   if (fileExists(lfi->filename))
2999     return;
3000
3001   // check for native Boulder Dash level file
3002   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
3003   if (fileExists(lfi->filename))
3004     return;
3005
3006   // check for Emerald Mine level file (V1)
3007   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
3008                                        'a' + (nr / 10) % 26, '0' + nr % 10);
3009   if (fileExists(lfi->filename))
3010     return;
3011   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
3012                                        'A' + (nr / 10) % 26, '0' + nr % 10);
3013   if (fileExists(lfi->filename))
3014     return;
3015
3016   // check for Emerald Mine level file (V2 to V5)
3017   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
3018   if (fileExists(lfi->filename))
3019     return;
3020
3021   // check for Emerald Mine level file (V6 / single mode)
3022   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3023   if (fileExists(lfi->filename))
3024     return;
3025   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3026   if (fileExists(lfi->filename))
3027     return;
3028
3029   // check for Emerald Mine level file (V6 / teamwork mode)
3030   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3031   if (fileExists(lfi->filename))
3032     return;
3033   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3034   if (fileExists(lfi->filename))
3035     return;
3036
3037   // check for various packed level file formats
3038   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3039   if (fileExists(lfi->filename))
3040     return;
3041
3042   // no known level file found -- use default values (and fail later)
3043   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3044                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
3045 }
3046
3047 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3048 {
3049   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3050     lfi->type = getFileTypeFromBasename(lfi->basename);
3051
3052   if (lfi->type == LEVEL_FILE_TYPE_RND)
3053     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3054 }
3055
3056 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3057 {
3058   // always start with reliable default values
3059   setFileInfoToDefaults(level_file_info);
3060
3061   level_file_info->nr = nr;     // set requested level number
3062
3063   determineLevelFileInfo_Filename(level_file_info);
3064   determineLevelFileInfo_Filetype(level_file_info);
3065 }
3066
3067 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3068                               struct LevelFileInfo *lfi_to)
3069 {
3070   lfi_to->nr     = lfi_from->nr;
3071   lfi_to->type   = lfi_from->type;
3072   lfi_to->packed = lfi_from->packed;
3073
3074   setString(&lfi_to->basename, lfi_from->basename);
3075   setString(&lfi_to->filename, lfi_from->filename);
3076 }
3077
3078 // ----------------------------------------------------------------------------
3079 // functions for loading R'n'D level
3080 // ----------------------------------------------------------------------------
3081
3082 int getMappedElement(int element)
3083 {
3084   // remap some (historic, now obsolete) elements
3085
3086   switch (element)
3087   {
3088     case EL_PLAYER_OBSOLETE:
3089       element = EL_PLAYER_1;
3090       break;
3091
3092     case EL_KEY_OBSOLETE:
3093       element = EL_KEY_1;
3094       break;
3095
3096     case EL_EM_KEY_1_FILE_OBSOLETE:
3097       element = EL_EM_KEY_1;
3098       break;
3099
3100     case EL_EM_KEY_2_FILE_OBSOLETE:
3101       element = EL_EM_KEY_2;
3102       break;
3103
3104     case EL_EM_KEY_3_FILE_OBSOLETE:
3105       element = EL_EM_KEY_3;
3106       break;
3107
3108     case EL_EM_KEY_4_FILE_OBSOLETE:
3109       element = EL_EM_KEY_4;
3110       break;
3111
3112     case EL_ENVELOPE_OBSOLETE:
3113       element = EL_ENVELOPE_1;
3114       break;
3115
3116     case EL_SP_EMPTY:
3117       element = EL_EMPTY;
3118       break;
3119
3120     default:
3121       if (element >= NUM_FILE_ELEMENTS)
3122       {
3123         Warn("invalid level element %d", element);
3124
3125         element = EL_UNKNOWN;
3126       }
3127       break;
3128   }
3129
3130   return element;
3131 }
3132
3133 static int getMappedElementByVersion(int element, int game_version)
3134 {
3135   // remap some elements due to certain game version
3136
3137   if (game_version <= VERSION_IDENT(2,2,0,0))
3138   {
3139     // map game font elements
3140     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3141                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3142                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3143                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3144   }
3145
3146   if (game_version < VERSION_IDENT(3,0,0,0))
3147   {
3148     // map Supaplex gravity tube elements
3149     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3150                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3151                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3152                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3153                element);
3154   }
3155
3156   return element;
3157 }
3158
3159 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3160 {
3161   level->file_version = getFileVersion(file);
3162   level->game_version = getFileVersion(file);
3163
3164   return chunk_size;
3165 }
3166
3167 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3168 {
3169   level->creation_date.year  = getFile16BitBE(file);
3170   level->creation_date.month = getFile8Bit(file);
3171   level->creation_date.day   = getFile8Bit(file);
3172
3173   level->creation_date.src   = DATE_SRC_LEVELFILE;
3174
3175   return chunk_size;
3176 }
3177
3178 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3179 {
3180   int initial_player_stepsize;
3181   int initial_player_gravity;
3182   int i, x, y;
3183
3184   level->fieldx = getFile8Bit(file);
3185   level->fieldy = getFile8Bit(file);
3186
3187   level->time           = getFile16BitBE(file);
3188   level->gems_needed    = getFile16BitBE(file);
3189
3190   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3191     level->name[i] = getFile8Bit(file);
3192   level->name[MAX_LEVEL_NAME_LEN] = 0;
3193
3194   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3195     level->score[i] = getFile8Bit(file);
3196
3197   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3198   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3199     for (y = 0; y < 3; y++)
3200       for (x = 0; x < 3; x++)
3201         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3202
3203   level->amoeba_speed           = getFile8Bit(file);
3204   level->time_magic_wall        = getFile8Bit(file);
3205   level->time_wheel             = getFile8Bit(file);
3206   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3207
3208   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3209                                    STEPSIZE_NORMAL);
3210
3211   for (i = 0; i < MAX_PLAYERS; i++)
3212     level->initial_player_stepsize[i] = initial_player_stepsize;
3213
3214   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3215
3216   for (i = 0; i < MAX_PLAYERS; i++)
3217     level->initial_player_gravity[i] = initial_player_gravity;
3218
3219   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3220   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3221
3222   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3223
3224   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3225   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3226   level->can_move_into_acid_bits = getFile32BitBE(file);
3227   level->dont_collide_with_bits = getFile8Bit(file);
3228
3229   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3230   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3231
3232   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3233   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3234   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3235
3236   level->game_engine_type       = getFile8Bit(file);
3237
3238   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3239
3240   return chunk_size;
3241 }
3242
3243 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3244 {
3245   int i;
3246
3247   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3248     level->name[i] = getFile8Bit(file);
3249   level->name[MAX_LEVEL_NAME_LEN] = 0;
3250
3251   return chunk_size;
3252 }
3253
3254 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3255 {
3256   int i;
3257
3258   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3259     level->author[i] = getFile8Bit(file);
3260   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3261
3262   return chunk_size;
3263 }
3264
3265 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3266 {
3267   int x, y;
3268   int chunk_size_expected = level->fieldx * level->fieldy;
3269
3270   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3271      stored with 16-bit encoding (and should be twice as big then).
3272      Even worse, playfield data was stored 16-bit when only yamyam content
3273      contained 16-bit elements and vice versa. */
3274
3275   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3276     chunk_size_expected *= 2;
3277
3278   if (chunk_size_expected != chunk_size)
3279   {
3280     ReadUnusedBytesFromFile(file, chunk_size);
3281     return chunk_size_expected;
3282   }
3283
3284   for (y = 0; y < level->fieldy; y++)
3285     for (x = 0; x < level->fieldx; x++)
3286       level->field[x][y] =
3287         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3288                          getFile8Bit(file));
3289   return chunk_size;
3290 }
3291
3292 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3293 {
3294   int i, x, y;
3295   int header_size = 4;
3296   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3297   int chunk_size_expected = header_size + content_size;
3298
3299   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3300      stored with 16-bit encoding (and should be twice as big then).
3301      Even worse, playfield data was stored 16-bit when only yamyam content
3302      contained 16-bit elements and vice versa. */
3303
3304   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3305     chunk_size_expected += content_size;
3306
3307   if (chunk_size_expected != chunk_size)
3308   {
3309     ReadUnusedBytesFromFile(file, chunk_size);
3310     return chunk_size_expected;
3311   }
3312
3313   getFile8Bit(file);
3314   level->num_yamyam_contents = getFile8Bit(file);
3315   getFile8Bit(file);
3316   getFile8Bit(file);
3317
3318   // correct invalid number of content fields -- should never happen
3319   if (level->num_yamyam_contents < 1 ||
3320       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3321     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3322
3323   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3324     for (y = 0; y < 3; y++)
3325       for (x = 0; x < 3; x++)
3326         level->yamyam_content[i].e[x][y] =
3327           getMappedElement(level->encoding_16bit_field ?
3328                            getFile16BitBE(file) : getFile8Bit(file));
3329   return chunk_size;
3330 }
3331
3332 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3333 {
3334   int i, x, y;
3335   int element;
3336   int num_contents;
3337   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3338
3339   element = getMappedElement(getFile16BitBE(file));
3340   num_contents = getFile8Bit(file);
3341
3342   getFile8Bit(file);    // content x size (unused)
3343   getFile8Bit(file);    // content y size (unused)
3344
3345   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3346
3347   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3348     for (y = 0; y < 3; y++)
3349       for (x = 0; x < 3; x++)
3350         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3351
3352   // correct invalid number of content fields -- should never happen
3353   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3354     num_contents = STD_ELEMENT_CONTENTS;
3355
3356   if (element == EL_YAMYAM)
3357   {
3358     level->num_yamyam_contents = num_contents;
3359
3360     for (i = 0; i < num_contents; i++)
3361       for (y = 0; y < 3; y++)
3362         for (x = 0; x < 3; x++)
3363           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3364   }
3365   else if (element == EL_BD_AMOEBA)
3366   {
3367     level->amoeba_content = content_array[0][0][0];
3368   }
3369   else
3370   {
3371     Warn("cannot load content for element '%d'", element);
3372   }
3373
3374   return chunk_size;
3375 }
3376
3377 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3378 {
3379   int i;
3380   int element;
3381   int envelope_nr;
3382   int envelope_len;
3383   int chunk_size_expected;
3384
3385   element = getMappedElement(getFile16BitBE(file));
3386   if (!IS_ENVELOPE(element))
3387     element = EL_ENVELOPE_1;
3388
3389   envelope_nr = element - EL_ENVELOPE_1;
3390
3391   envelope_len = getFile16BitBE(file);
3392
3393   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3394   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3395
3396   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3397
3398   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3399   if (chunk_size_expected != chunk_size)
3400   {
3401     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3402     return chunk_size_expected;
3403   }
3404
3405   for (i = 0; i < envelope_len; i++)
3406     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3407
3408   return chunk_size;
3409 }
3410
3411 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3412 {
3413   int num_changed_custom_elements = getFile16BitBE(file);
3414   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3415   int i;
3416
3417   if (chunk_size_expected != chunk_size)
3418   {
3419     ReadUnusedBytesFromFile(file, chunk_size - 2);
3420     return chunk_size_expected;
3421   }
3422
3423   for (i = 0; i < num_changed_custom_elements; i++)
3424   {
3425     int element = getMappedElement(getFile16BitBE(file));
3426     int properties = getFile32BitBE(file);
3427
3428     if (IS_CUSTOM_ELEMENT(element))
3429       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3430     else
3431       Warn("invalid custom element number %d", element);
3432
3433     // older game versions that wrote level files with CUS1 chunks used
3434     // different default push delay values (not yet stored in level file)
3435     element_info[element].push_delay_fixed = 2;
3436     element_info[element].push_delay_random = 8;
3437   }
3438
3439   level->file_has_custom_elements = TRUE;
3440
3441   return chunk_size;
3442 }
3443
3444 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3445 {
3446   int num_changed_custom_elements = getFile16BitBE(file);
3447   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3448   int i;
3449
3450   if (chunk_size_expected != chunk_size)
3451   {
3452     ReadUnusedBytesFromFile(file, chunk_size - 2);
3453     return chunk_size_expected;
3454   }
3455
3456   for (i = 0; i < num_changed_custom_elements; i++)
3457   {
3458     int element = getMappedElement(getFile16BitBE(file));
3459     int custom_target_element = getMappedElement(getFile16BitBE(file));
3460
3461     if (IS_CUSTOM_ELEMENT(element))
3462       element_info[element].change->target_element = custom_target_element;
3463     else
3464       Warn("invalid custom element number %d", element);
3465   }
3466
3467   level->file_has_custom_elements = TRUE;
3468
3469   return chunk_size;
3470 }
3471
3472 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3473 {
3474   int num_changed_custom_elements = getFile16BitBE(file);
3475   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3476   int i, j, x, y;
3477
3478   if (chunk_size_expected != chunk_size)
3479   {
3480     ReadUnusedBytesFromFile(file, chunk_size - 2);
3481     return chunk_size_expected;
3482   }
3483
3484   for (i = 0; i < num_changed_custom_elements; i++)
3485   {
3486     int element = getMappedElement(getFile16BitBE(file));
3487     struct ElementInfo *ei = &element_info[element];
3488     unsigned int event_bits;
3489
3490     if (!IS_CUSTOM_ELEMENT(element))
3491     {
3492       Warn("invalid custom element number %d", element);
3493
3494       element = EL_INTERNAL_DUMMY;
3495     }
3496
3497     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3498       ei->description[j] = getFile8Bit(file);
3499     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3500
3501     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3502
3503     // some free bytes for future properties and padding
3504     ReadUnusedBytesFromFile(file, 7);
3505
3506     ei->use_gfx_element = getFile8Bit(file);
3507     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3508
3509     ei->collect_score_initial = getFile8Bit(file);
3510     ei->collect_count_initial = getFile8Bit(file);
3511
3512     ei->push_delay_fixed = getFile16BitBE(file);
3513     ei->push_delay_random = getFile16BitBE(file);
3514     ei->move_delay_fixed = getFile16BitBE(file);
3515     ei->move_delay_random = getFile16BitBE(file);
3516
3517     ei->move_pattern = getFile16BitBE(file);
3518     ei->move_direction_initial = getFile8Bit(file);
3519     ei->move_stepsize = getFile8Bit(file);
3520
3521     for (y = 0; y < 3; y++)
3522       for (x = 0; x < 3; x++)
3523         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3524
3525     // bits 0 - 31 of "has_event[]"
3526     event_bits = getFile32BitBE(file);
3527     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3528       if (event_bits & (1u << j))
3529         ei->change->has_event[j] = TRUE;
3530
3531     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3532
3533     ei->change->delay_fixed = getFile16BitBE(file);
3534     ei->change->delay_random = getFile16BitBE(file);
3535     ei->change->delay_frames = getFile16BitBE(file);
3536
3537     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3538
3539     ei->change->explode = getFile8Bit(file);
3540     ei->change->use_target_content = getFile8Bit(file);
3541     ei->change->only_if_complete = getFile8Bit(file);
3542     ei->change->use_random_replace = getFile8Bit(file);
3543
3544     ei->change->random_percentage = getFile8Bit(file);
3545     ei->change->replace_when = getFile8Bit(file);
3546
3547     for (y = 0; y < 3; y++)
3548       for (x = 0; x < 3; x++)
3549         ei->change->target_content.e[x][y] =
3550           getMappedElement(getFile16BitBE(file));
3551
3552     ei->slippery_type = getFile8Bit(file);
3553
3554     // some free bytes for future properties and padding
3555     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3556
3557     // mark that this custom element has been modified
3558     ei->modified_settings = TRUE;
3559   }
3560
3561   level->file_has_custom_elements = TRUE;
3562
3563   return chunk_size;
3564 }
3565
3566 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3567 {
3568   struct ElementInfo *ei;
3569   int chunk_size_expected;
3570   int element;
3571   int i, j, x, y;
3572
3573   // ---------- custom element base property values (96 bytes) ----------------
3574
3575   element = getMappedElement(getFile16BitBE(file));
3576
3577   if (!IS_CUSTOM_ELEMENT(element))
3578   {
3579     Warn("invalid custom element number %d", element);
3580
3581     ReadUnusedBytesFromFile(file, chunk_size - 2);
3582
3583     return chunk_size;
3584   }
3585
3586   ei = &element_info[element];
3587
3588   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3589     ei->description[i] = getFile8Bit(file);
3590   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3591
3592   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3593
3594   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3595
3596   ei->num_change_pages = getFile8Bit(file);
3597
3598   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3599   if (chunk_size_expected != chunk_size)
3600   {
3601     ReadUnusedBytesFromFile(file, chunk_size - 43);
3602     return chunk_size_expected;
3603   }
3604
3605   ei->ce_value_fixed_initial = getFile16BitBE(file);
3606   ei->ce_value_random_initial = getFile16BitBE(file);
3607   ei->use_last_ce_value = getFile8Bit(file);
3608
3609   ei->use_gfx_element = getFile8Bit(file);
3610   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3611
3612   ei->collect_score_initial = getFile8Bit(file);
3613   ei->collect_count_initial = getFile8Bit(file);
3614
3615   ei->drop_delay_fixed = getFile8Bit(file);
3616   ei->push_delay_fixed = getFile8Bit(file);
3617   ei->drop_delay_random = getFile8Bit(file);
3618   ei->push_delay_random = getFile8Bit(file);
3619   ei->move_delay_fixed = getFile16BitBE(file);
3620   ei->move_delay_random = getFile16BitBE(file);
3621
3622   // bits 0 - 15 of "move_pattern" ...
3623   ei->move_pattern = getFile16BitBE(file);
3624   ei->move_direction_initial = getFile8Bit(file);
3625   ei->move_stepsize = getFile8Bit(file);
3626
3627   ei->slippery_type = getFile8Bit(file);
3628
3629   for (y = 0; y < 3; y++)
3630     for (x = 0; x < 3; x++)
3631       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3632
3633   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3634   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3635   ei->move_leave_type = getFile8Bit(file);
3636
3637   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3638   ei->move_pattern |= (getFile16BitBE(file) << 16);
3639
3640   ei->access_direction = getFile8Bit(file);
3641
3642   ei->explosion_delay = getFile8Bit(file);
3643   ei->ignition_delay = getFile8Bit(file);
3644   ei->explosion_type = getFile8Bit(file);
3645
3646   // some free bytes for future custom property values and padding
3647   ReadUnusedBytesFromFile(file, 1);
3648
3649   // ---------- change page property values (48 bytes) ------------------------
3650
3651   setElementChangePages(ei, ei->num_change_pages);
3652
3653   for (i = 0; i < ei->num_change_pages; i++)
3654   {
3655     struct ElementChangeInfo *change = &ei->change_page[i];
3656     unsigned int event_bits;
3657
3658     // always start with reliable default values
3659     setElementChangeInfoToDefaults(change);
3660
3661     // bits 0 - 31 of "has_event[]" ...
3662     event_bits = getFile32BitBE(file);
3663     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3664       if (event_bits & (1u << j))
3665         change->has_event[j] = TRUE;
3666
3667     change->target_element = getMappedElement(getFile16BitBE(file));
3668
3669     change->delay_fixed = getFile16BitBE(file);
3670     change->delay_random = getFile16BitBE(file);
3671     change->delay_frames = getFile16BitBE(file);
3672
3673     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3674
3675     change->explode = getFile8Bit(file);
3676     change->use_target_content = getFile8Bit(file);
3677     change->only_if_complete = getFile8Bit(file);
3678     change->use_random_replace = getFile8Bit(file);
3679
3680     change->random_percentage = getFile8Bit(file);
3681     change->replace_when = getFile8Bit(file);
3682
3683     for (y = 0; y < 3; y++)
3684       for (x = 0; x < 3; x++)
3685         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3686
3687     change->can_change = getFile8Bit(file);
3688
3689     change->trigger_side = getFile8Bit(file);
3690
3691     change->trigger_player = getFile8Bit(file);
3692     change->trigger_page = getFile8Bit(file);
3693
3694     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3695                             CH_PAGE_ANY : (1 << change->trigger_page));
3696
3697     change->has_action = getFile8Bit(file);
3698     change->action_type = getFile8Bit(file);
3699     change->action_mode = getFile8Bit(file);
3700     change->action_arg = getFile16BitBE(file);
3701
3702     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3703     event_bits = getFile8Bit(file);
3704     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3705       if (event_bits & (1u << (j - 32)))
3706         change->has_event[j] = TRUE;
3707   }
3708
3709   // mark this custom element as modified
3710   ei->modified_settings = TRUE;
3711
3712   level->file_has_custom_elements = TRUE;
3713
3714   return chunk_size;
3715 }
3716
3717 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3718 {
3719   struct ElementInfo *ei;
3720   struct ElementGroupInfo *group;
3721   int element;
3722   int i;
3723
3724   element = getMappedElement(getFile16BitBE(file));
3725
3726   if (!IS_GROUP_ELEMENT(element))
3727   {
3728     Warn("invalid group element number %d", element);
3729
3730     ReadUnusedBytesFromFile(file, chunk_size - 2);
3731
3732     return chunk_size;
3733   }
3734
3735   ei = &element_info[element];
3736
3737   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3738     ei->description[i] = getFile8Bit(file);
3739   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3740
3741   group = element_info[element].group;
3742
3743   group->num_elements = getFile8Bit(file);
3744
3745   ei->use_gfx_element = getFile8Bit(file);
3746   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3747
3748   group->choice_mode = getFile8Bit(file);
3749
3750   // some free bytes for future values and padding
3751   ReadUnusedBytesFromFile(file, 3);
3752
3753   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3754     group->element[i] = getMappedElement(getFile16BitBE(file));
3755
3756   // mark this group element as modified
3757   element_info[element].modified_settings = TRUE;
3758
3759   level->file_has_custom_elements = TRUE;
3760
3761   return chunk_size;
3762 }
3763
3764 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3765                                 int element, int real_element)
3766 {
3767   int micro_chunk_size = 0;
3768   int conf_type = getFile8Bit(file);
3769   int byte_mask = conf_type & CONF_MASK_BYTES;
3770   boolean element_found = FALSE;
3771   int i;
3772
3773   micro_chunk_size += 1;
3774
3775   if (byte_mask == CONF_MASK_MULTI_BYTES)
3776   {
3777     int num_bytes = getFile16BitBE(file);
3778     byte *buffer = checked_malloc(num_bytes);
3779
3780     ReadBytesFromFile(file, buffer, num_bytes);
3781
3782     for (i = 0; conf[i].data_type != -1; i++)
3783     {
3784       if (conf[i].element == element &&
3785           conf[i].conf_type == conf_type)
3786       {
3787         int data_type = conf[i].data_type;
3788         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3789         int max_num_entities = conf[i].max_num_entities;
3790
3791         if (num_entities > max_num_entities)
3792         {
3793           Warn("truncating number of entities for element %d from %d to %d",
3794                element, num_entities, max_num_entities);
3795
3796           num_entities = max_num_entities;
3797         }
3798
3799         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3800                                   data_type == TYPE_CONTENT_LIST))
3801         {
3802           // for element and content lists, zero entities are not allowed
3803           Warn("found empty list of entities for element %d", element);
3804
3805           // do not set "num_entities" here to prevent reading behind buffer
3806
3807           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3808         }
3809         else
3810         {
3811           *(int *)(conf[i].num_entities) = num_entities;
3812         }
3813
3814         element_found = TRUE;
3815
3816         if (data_type == TYPE_STRING)
3817         {
3818           char *string = (char *)(conf[i].value);
3819           int j;
3820
3821           for (j = 0; j < max_num_entities; j++)
3822             string[j] = (j < num_entities ? buffer[j] : '\0');
3823         }
3824         else if (data_type == TYPE_ELEMENT_LIST)
3825         {
3826           int *element_array = (int *)(conf[i].value);
3827           int j;
3828
3829           for (j = 0; j < num_entities; j++)
3830             element_array[j] =
3831               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3832         }
3833         else if (data_type == TYPE_CONTENT_LIST)
3834         {
3835           struct Content *content= (struct Content *)(conf[i].value);
3836           int c, x, y;
3837
3838           for (c = 0; c < num_entities; c++)
3839             for (y = 0; y < 3; y++)
3840               for (x = 0; x < 3; x++)
3841                 content[c].e[x][y] =
3842                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3843         }
3844         else
3845           element_found = FALSE;
3846
3847         break;
3848       }
3849     }
3850
3851     checked_free(buffer);
3852
3853     micro_chunk_size += 2 + num_bytes;
3854   }
3855   else          // constant size configuration data (1, 2 or 4 bytes)
3856   {
3857     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3858                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3859                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3860
3861     for (i = 0; conf[i].data_type != -1; i++)
3862     {
3863       if (conf[i].element == element &&
3864           conf[i].conf_type == conf_type)
3865       {
3866         int data_type = conf[i].data_type;
3867
3868         if (data_type == TYPE_ELEMENT)
3869           value = getMappedElement(value);
3870
3871         if (data_type == TYPE_BOOLEAN)
3872           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3873         else
3874           *(int *)    (conf[i].value) = value;
3875
3876         element_found = TRUE;
3877
3878         break;
3879       }
3880     }
3881
3882     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3883   }
3884
3885   if (!element_found)
3886   {
3887     char *error_conf_chunk_bytes =
3888       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3889        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3890        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3891     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3892     int error_element = real_element;
3893
3894     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3895          error_conf_chunk_bytes, error_conf_chunk_token,
3896          error_element, EL_NAME(error_element));
3897   }
3898
3899   return micro_chunk_size;
3900 }
3901
3902 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3903 {
3904   int real_chunk_size = 0;
3905
3906   li = *level;          // copy level data into temporary buffer
3907
3908   while (!checkEndOfFile(file))
3909   {
3910     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3911
3912     if (real_chunk_size >= chunk_size)
3913       break;
3914   }
3915
3916   *level = li;          // copy temporary buffer back to level data
3917
3918   return real_chunk_size;
3919 }
3920
3921 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3922 {
3923   int real_chunk_size = 0;
3924
3925   li = *level;          // copy level data into temporary buffer
3926
3927   while (!checkEndOfFile(file))
3928   {
3929     int element = getMappedElement(getFile16BitBE(file));
3930
3931     real_chunk_size += 2;
3932     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3933                                             element, element);
3934     if (real_chunk_size >= chunk_size)
3935       break;
3936   }
3937
3938   *level = li;          // copy temporary buffer back to level data
3939
3940   return real_chunk_size;
3941 }
3942
3943 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3944 {
3945   int real_chunk_size = 0;
3946
3947   li = *level;          // copy level data into temporary buffer
3948
3949   while (!checkEndOfFile(file))
3950   {
3951     int element = getMappedElement(getFile16BitBE(file));
3952
3953     real_chunk_size += 2;
3954     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3955                                             element, element);
3956     if (real_chunk_size >= chunk_size)
3957       break;
3958   }
3959
3960   *level = li;          // copy temporary buffer back to level data
3961
3962   return real_chunk_size;
3963 }
3964
3965 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3966 {
3967   int element = getMappedElement(getFile16BitBE(file));
3968   int envelope_nr = element - EL_ENVELOPE_1;
3969   int real_chunk_size = 2;
3970
3971   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3972
3973   while (!checkEndOfFile(file))
3974   {
3975     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3976                                             -1, element);
3977
3978     if (real_chunk_size >= chunk_size)
3979       break;
3980   }
3981
3982   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3983
3984   return real_chunk_size;
3985 }
3986
3987 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3988 {
3989   int element = getMappedElement(getFile16BitBE(file));
3990   int real_chunk_size = 2;
3991   struct ElementInfo *ei = &element_info[element];
3992   int i;
3993
3994   xx_ei = *ei;          // copy element data into temporary buffer
3995
3996   xx_ei.num_change_pages = -1;
3997
3998   while (!checkEndOfFile(file))
3999   {
4000     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
4001                                             -1, element);
4002     if (xx_ei.num_change_pages != -1)
4003       break;
4004
4005     if (real_chunk_size >= chunk_size)
4006       break;
4007   }
4008
4009   *ei = xx_ei;
4010
4011   if (ei->num_change_pages == -1)
4012   {
4013     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4014          EL_NAME(element));
4015
4016     ei->num_change_pages = 1;
4017
4018     setElementChangePages(ei, 1);
4019     setElementChangeInfoToDefaults(ei->change);
4020
4021     return real_chunk_size;
4022   }
4023
4024   // initialize number of change pages stored for this custom element
4025   setElementChangePages(ei, ei->num_change_pages);
4026   for (i = 0; i < ei->num_change_pages; i++)
4027     setElementChangeInfoToDefaults(&ei->change_page[i]);
4028
4029   // start with reading properties for the first change page
4030   xx_current_change_page = 0;
4031
4032   while (!checkEndOfFile(file))
4033   {
4034     // level file might contain invalid change page number
4035     if (xx_current_change_page >= ei->num_change_pages)
4036       break;
4037
4038     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4039
4040     xx_change = *change;        // copy change data into temporary buffer
4041
4042     resetEventBits();           // reset bits; change page might have changed
4043
4044     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4045                                             -1, element);
4046
4047     *change = xx_change;
4048
4049     setEventFlagsFromEventBits(change);
4050
4051     if (real_chunk_size >= chunk_size)
4052       break;
4053   }
4054
4055   level->file_has_custom_elements = TRUE;
4056
4057   return real_chunk_size;
4058 }
4059
4060 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4061 {
4062   int element = getMappedElement(getFile16BitBE(file));
4063   int real_chunk_size = 2;
4064   struct ElementInfo *ei = &element_info[element];
4065   struct ElementGroupInfo *group = ei->group;
4066
4067   if (group == NULL)
4068     return -1;
4069
4070   xx_ei = *ei;          // copy element data into temporary buffer
4071   xx_group = *group;    // copy group data into temporary buffer
4072
4073   while (!checkEndOfFile(file))
4074   {
4075     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4076                                             -1, element);
4077
4078     if (real_chunk_size >= chunk_size)
4079       break;
4080   }
4081
4082   *ei = xx_ei;
4083   *group = xx_group;
4084
4085   level->file_has_custom_elements = TRUE;
4086
4087   return real_chunk_size;
4088 }
4089
4090 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4091 {
4092   int element = getMappedElement(getFile16BitBE(file));
4093   int real_chunk_size = 2;
4094   struct ElementInfo *ei = &element_info[element];
4095
4096   xx_ei = *ei;          // copy element data into temporary buffer
4097
4098   while (!checkEndOfFile(file))
4099   {
4100     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4101                                             -1, element);
4102
4103     if (real_chunk_size >= chunk_size)
4104       break;
4105   }
4106
4107   *ei = xx_ei;
4108
4109   level->file_has_custom_elements = TRUE;
4110
4111   return real_chunk_size;
4112 }
4113
4114 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4115                                       struct LevelFileInfo *level_file_info,
4116                                       boolean level_info_only)
4117 {
4118   char *filename = level_file_info->filename;
4119   char cookie[MAX_LINE_LEN];
4120   char chunk_name[CHUNK_ID_LEN + 1];
4121   int chunk_size;
4122   File *file;
4123
4124   if (!(file = openFile(filename, MODE_READ)))
4125   {
4126     level->no_valid_file = TRUE;
4127     level->no_level_file = TRUE;
4128
4129     if (level_info_only)
4130       return;
4131
4132     Warn("cannot read level '%s' -- using empty level", filename);
4133
4134     if (!setup.editor.use_template_for_new_levels)
4135       return;
4136
4137     // if level file not found, try to initialize level data from template
4138     filename = getGlobalLevelTemplateFilename();
4139
4140     if (!(file = openFile(filename, MODE_READ)))
4141       return;
4142
4143     // default: for empty levels, use level template for custom elements
4144     level->use_custom_template = TRUE;
4145
4146     level->no_valid_file = FALSE;
4147   }
4148
4149   getFileChunkBE(file, chunk_name, NULL);
4150   if (strEqual(chunk_name, "RND1"))
4151   {
4152     getFile32BitBE(file);               // not used
4153
4154     getFileChunkBE(file, chunk_name, NULL);
4155     if (!strEqual(chunk_name, "CAVE"))
4156     {
4157       level->no_valid_file = TRUE;
4158
4159       Warn("unknown format of level file '%s'", filename);
4160
4161       closeFile(file);
4162
4163       return;
4164     }
4165   }
4166   else  // check for pre-2.0 file format with cookie string
4167   {
4168     strcpy(cookie, chunk_name);
4169     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4170       cookie[4] = '\0';
4171     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4172       cookie[strlen(cookie) - 1] = '\0';
4173
4174     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4175     {
4176       level->no_valid_file = TRUE;
4177
4178       Warn("unknown format of level file '%s'", filename);
4179
4180       closeFile(file);
4181
4182       return;
4183     }
4184
4185     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4186     {
4187       level->no_valid_file = TRUE;
4188
4189       Warn("unsupported version of level file '%s'", filename);
4190
4191       closeFile(file);
4192
4193       return;
4194     }
4195
4196     // pre-2.0 level files have no game version, so use file version here
4197     level->game_version = level->file_version;
4198   }
4199
4200   if (level->file_version < FILE_VERSION_1_2)
4201   {
4202     // level files from versions before 1.2.0 without chunk structure
4203     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4204     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4205   }
4206   else
4207   {
4208     static struct
4209     {
4210       char *name;
4211       int size;
4212       int (*loader)(File *, int, struct LevelInfo *);
4213     }
4214     chunk_info[] =
4215     {
4216       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4217       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4218       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4219       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4220       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4221       { "INFO", -1,                     LoadLevel_INFO },
4222       { "BODY", -1,                     LoadLevel_BODY },
4223       { "CONT", -1,                     LoadLevel_CONT },
4224       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4225       { "CNT3", -1,                     LoadLevel_CNT3 },
4226       { "CUS1", -1,                     LoadLevel_CUS1 },
4227       { "CUS2", -1,                     LoadLevel_CUS2 },
4228       { "CUS3", -1,                     LoadLevel_CUS3 },
4229       { "CUS4", -1,                     LoadLevel_CUS4 },
4230       { "GRP1", -1,                     LoadLevel_GRP1 },
4231       { "CONF", -1,                     LoadLevel_CONF },
4232       { "ELEM", -1,                     LoadLevel_ELEM },
4233       { "NOTE", -1,                     LoadLevel_NOTE },
4234       { "CUSX", -1,                     LoadLevel_CUSX },
4235       { "GRPX", -1,                     LoadLevel_GRPX },
4236       { "EMPX", -1,                     LoadLevel_EMPX },
4237
4238       {  NULL,  0,                      NULL }
4239     };
4240
4241     while (getFileChunkBE(file, chunk_name, &chunk_size))
4242     {
4243       int i = 0;
4244
4245       while (chunk_info[i].name != NULL &&
4246              !strEqual(chunk_name, chunk_info[i].name))
4247         i++;
4248
4249       if (chunk_info[i].name == NULL)
4250       {
4251         Warn("unknown chunk '%s' in level file '%s'",
4252              chunk_name, filename);
4253
4254         ReadUnusedBytesFromFile(file, chunk_size);
4255       }
4256       else if (chunk_info[i].size != -1 &&
4257                chunk_info[i].size != chunk_size)
4258       {
4259         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4260              chunk_size, chunk_name, filename);
4261
4262         ReadUnusedBytesFromFile(file, chunk_size);
4263       }
4264       else
4265       {
4266         // call function to load this level chunk
4267         int chunk_size_expected =
4268           (chunk_info[i].loader)(file, chunk_size, level);
4269
4270         if (chunk_size_expected < 0)
4271         {
4272           Warn("error reading chunk '%s' in level file '%s'",
4273                chunk_name, filename);
4274
4275           break;
4276         }
4277
4278         // the size of some chunks cannot be checked before reading other
4279         // chunks first (like "HEAD" and "BODY") that contain some header
4280         // information, so check them here
4281         if (chunk_size_expected != chunk_size)
4282         {
4283           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4284                chunk_size, chunk_name, filename);
4285
4286           break;
4287         }
4288       }
4289     }
4290   }
4291
4292   closeFile(file);
4293 }
4294
4295
4296 // ----------------------------------------------------------------------------
4297 // functions for loading BD level
4298 // ----------------------------------------------------------------------------
4299
4300 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4301 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4302
4303 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4304 {
4305   struct LevelInfo_BD *level_bd = level->native_bd_level;
4306   GdCave *cave = NULL;  // will be changed below
4307   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4308   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4309   int x, y;
4310
4311   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4312
4313   // cave and map newly allocated when set to defaults above
4314   cave = level_bd->cave;
4315
4316   // level type
4317   cave->intermission                    = level->bd_intermission;
4318
4319   // level settings
4320   cave->level_time[0]                   = level->time;
4321   cave->level_diamonds[0]               = level->gems_needed;
4322
4323   // game timing
4324   cave->scheduling                      = level->bd_scheduling_type;
4325   cave->pal_timing                      = level->bd_pal_timing;
4326   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4327   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4328   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4329   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4330
4331   // scores
4332   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4333   cave->diamond_value                   = level->score[SC_EMERALD];
4334   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4335
4336   // compatibility settings
4337   cave->lineshift                       = level->bd_line_shifting_borders;
4338   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4339   cave->short_explosions                = level->bd_short_explosions;
4340
4341   // player properties
4342   cave->diagonal_movements              = level->bd_diagonal_movements;
4343   cave->active_is_first_found           = level->bd_topmost_player_active;
4344   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4345   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4346   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4347   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4348
4349   // element properties
4350   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4351   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4352   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4353   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4354   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4355   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4356   cave->level_magic_wall_time[0]        = level->bd_magic_wall_time;
4357   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4358   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4359   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4360   cave->magic_wall_breakscan            = level->bd_magic_wall_break_scan;
4361
4362   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4363   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4364   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4365   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4366   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4367   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4368   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4369
4370   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4371   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4372   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4373   cave->level_amoeba_threshold[0]       = level->bd_amoeba_1_threshold_too_big;
4374   cave->level_amoeba_time[0]            = level->bd_amoeba_1_slow_growth_time;
4375   cave->amoeba_growth_prob              = level->bd_amoeba_1_slow_growth_rate * 10000;
4376   cave->amoeba_fast_growth_prob         = level->bd_amoeba_1_fast_growth_rate * 10000;
4377   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4378   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4379   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4380   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4381
4382   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_1_content_too_big);
4383   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_1_content_enclosed);
4384   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4385   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4386   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4387   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4388
4389   cave->slime_predictable               = level->bd_slime_is_predictable;
4390   cave->slime_correct_random            = level->bd_slime_correct_random;
4391   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4392   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4393   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4394   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4395   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4396   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4397   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4398   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4399   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4400   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4401
4402   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4403   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4404   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4405
4406   cave->biter_delay_frame               = level->bd_biter_move_delay;
4407   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4408
4409   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4410
4411   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4412
4413   cave->replicators_active              = level->bd_replicators_active;
4414   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4415
4416   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4417   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4418
4419   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4420
4421   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4422
4423   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4424   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4425   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4426
4427   cave->infinite_rockets                = level->bd_infinite_rockets;
4428
4429   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4430   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4431
4432   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4433   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4434
4435   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4436   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4437   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4438
4439   cave->gravity                         = level->bd_gravity_direction;
4440   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4441   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4442   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4443
4444   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4445   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4446   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4447   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4448
4449   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_1_explodes_to);
4450   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4451   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_1_explodes_to);
4452   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4453   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4454   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4455
4456   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4457   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4458   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4459   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4460
4461   cave->colorb                          = level->bd_color_b;
4462   cave->color0                          = level->bd_color_0;
4463   cave->color1                          = level->bd_color_1;
4464   cave->color2                          = level->bd_color_2;
4465   cave->color3                          = level->bd_color_3;
4466   cave->color4                          = level->bd_color_4;
4467   cave->color5                          = level->bd_color_5;
4468
4469   // level name
4470   strncpy(cave->name, level->name, sizeof(GdString));
4471   cave->name[sizeof(GdString) - 1] = '\0';
4472
4473   // playfield elements
4474   for (x = 0; x < cave->w; x++)
4475     for (y = 0; y < cave->h; y++)
4476       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4477 }
4478
4479 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4480 {
4481   struct LevelInfo_BD *level_bd = level->native_bd_level;
4482   GdCave *cave = level_bd->cave;
4483   int bd_level_nr = level_bd->level_nr;
4484   int x, y;
4485
4486   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4487   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4488
4489   // level type
4490   level->bd_intermission                = cave->intermission;
4491
4492   // level settings
4493   level->time                           = cave->level_time[bd_level_nr];
4494   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4495
4496   // game timing
4497   level->bd_scheduling_type             = cave->scheduling;
4498   level->bd_pal_timing                  = cave->pal_timing;
4499   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4500   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4501   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4502   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4503
4504   // scores
4505   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4506   level->score[SC_EMERALD]              = cave->diamond_value;
4507   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4508
4509   // compatibility settings
4510   level->bd_line_shifting_borders       = cave->lineshift;
4511   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4512   level->bd_short_explosions            = cave->short_explosions;
4513
4514   // player properties
4515   level->bd_diagonal_movements          = cave->diagonal_movements;
4516   level->bd_topmost_player_active       = cave->active_is_first_found;
4517   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4518   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4519   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4520   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4521
4522   // element properties
4523   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4524   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4525   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4526   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4527   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4528   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4529   level->bd_magic_wall_time             = cave->level_magic_wall_time[bd_level_nr];
4530   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4531   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4532   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4533   level->bd_magic_wall_break_scan       = cave->magic_wall_breakscan;
4534
4535   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4536   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4537   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4538   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4539   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4540   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4541   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4542
4543   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4544   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4545   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4546   level->bd_amoeba_1_threshold_too_big  = cave->level_amoeba_threshold[bd_level_nr];
4547   level->bd_amoeba_1_slow_growth_time   = cave->level_amoeba_time[bd_level_nr];
4548   level->bd_amoeba_1_slow_growth_rate   = cave->amoeba_growth_prob      / 10000;
4549   level->bd_amoeba_1_fast_growth_rate   = cave->amoeba_fast_growth_prob / 10000;
4550   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4551   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4552   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4553   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4554
4555   level->bd_amoeba_1_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4556   level->bd_amoeba_1_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4557   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4558   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4559   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4560   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4561
4562   level->bd_slime_is_predictable        = cave->slime_predictable;
4563   level->bd_slime_correct_random        = cave->slime_correct_random;
4564   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4565   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4566   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4567   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4568   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4569   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4570   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4571   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4572   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4573   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4574
4575   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4576   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4577   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4578
4579   level->bd_biter_move_delay            = cave->biter_delay_frame;
4580   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4581
4582   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4583
4584   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4585
4586   level->bd_replicators_active          = cave->replicators_active;
4587   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4588
4589   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4590   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4591
4592   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4593
4594   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4595
4596   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4597   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4598   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4599
4600   level->bd_infinite_rockets            = cave->infinite_rockets;
4601
4602   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4603   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4604
4605   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4606   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4607
4608   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4609   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4610   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4611
4612   level->bd_gravity_direction           = cave->gravity;
4613   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4614   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4615   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4616
4617   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4618   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4619   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4620   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4621
4622   level->bd_firefly_1_explodes_to       = CAVE_TO_LEVEL(cave->firefly_explode_to);
4623   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4624   level->bd_butterfly_1_explodes_to     = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4625   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4626   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4627   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4628
4629   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4630   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4631   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4632   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4633
4634   level->bd_color_b                     = cave->colorb;
4635   level->bd_color_0                     = cave->color0;
4636   level->bd_color_1                     = cave->color1;
4637   level->bd_color_2                     = cave->color2;
4638   level->bd_color_3                     = cave->color3;
4639   level->bd_color_4                     = cave->color4;
4640   level->bd_color_5                     = cave->color5;
4641
4642   // set default color type and colors for BD style level colors
4643   SetDefaultLevelColorType_BD();
4644   SetDefaultLevelColors_BD();
4645
4646   // level name
4647   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4648
4649   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4650   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4651
4652   // playfield elements
4653   for (x = 0; x < level->fieldx; x++)
4654     for (y = 0; y < level->fieldy; y++)
4655       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4656
4657   checked_free(cave_name);
4658 }
4659
4660 static void setTapeInfoToDefaults(void);
4661
4662 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4663 {
4664   struct LevelInfo_BD *level_bd = level->native_bd_level;
4665   GdCave *cave = level_bd->cave;
4666   GdReplay *replay = level_bd->replay;
4667   int i;
4668
4669   if (replay == NULL)
4670     return;
4671
4672   // always start with reliable default values
4673   setTapeInfoToDefaults();
4674
4675   tape.level_nr = level_nr;             // (currently not used)
4676   tape.random_seed = replay->seed;
4677
4678   TapeSetDateFromIsoDateString(replay->date);
4679
4680   tape.counter = 0;
4681   tape.pos[tape.counter].delay = 0;
4682
4683   tape.bd_replay = TRUE;
4684
4685   // all time calculations only used to display approximate tape time
4686   int cave_speed = cave->speed;
4687   int milliseconds_game = 0;
4688   int milliseconds_elapsed = 20;
4689
4690   for (i = 0; i < replay->movements->len; i++)
4691   {
4692     int replay_action = replay->movements->data[i];
4693     int tape_action = map_action_BD_to_RND(replay_action);
4694     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4695     boolean success = 0;
4696
4697     while (1)
4698     {
4699       success = TapeAddAction(action);
4700
4701       milliseconds_game += milliseconds_elapsed;
4702
4703       if (milliseconds_game >= cave_speed)
4704       {
4705         milliseconds_game -= cave_speed;
4706
4707         break;
4708       }
4709     }
4710
4711     tape.counter++;
4712     tape.pos[tape.counter].delay = 0;
4713     tape.pos[tape.counter].action[0] = 0;
4714
4715     if (!success)
4716     {
4717       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4718
4719       break;
4720     }
4721   }
4722
4723   TapeHaltRecording();
4724
4725   if (!replay->success)
4726     Warn("BD replay is marked as not successful");
4727 }
4728
4729
4730 // ----------------------------------------------------------------------------
4731 // functions for loading EM level
4732 // ----------------------------------------------------------------------------
4733
4734 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4735 {
4736   static int ball_xy[8][2] =
4737   {
4738     { 0, 0 },
4739     { 1, 0 },
4740     { 2, 0 },
4741     { 0, 1 },
4742     { 2, 1 },
4743     { 0, 2 },
4744     { 1, 2 },
4745     { 2, 2 },
4746   };
4747   struct LevelInfo_EM *level_em = level->native_em_level;
4748   struct CAVE *cav = level_em->cav;
4749   int i, j, x, y;
4750
4751   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4752   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4753
4754   cav->time_seconds     = level->time;
4755   cav->gems_needed      = level->gems_needed;
4756
4757   cav->emerald_score    = level->score[SC_EMERALD];
4758   cav->diamond_score    = level->score[SC_DIAMOND];
4759   cav->alien_score      = level->score[SC_ROBOT];
4760   cav->tank_score       = level->score[SC_SPACESHIP];
4761   cav->bug_score        = level->score[SC_BUG];
4762   cav->eater_score      = level->score[SC_YAMYAM];
4763   cav->nut_score        = level->score[SC_NUT];
4764   cav->dynamite_score   = level->score[SC_DYNAMITE];
4765   cav->key_score        = level->score[SC_KEY];
4766   cav->exit_score       = level->score[SC_TIME_BONUS];
4767
4768   cav->num_eater_arrays = level->num_yamyam_contents;
4769
4770   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4771     for (y = 0; y < 3; y++)
4772       for (x = 0; x < 3; x++)
4773         cav->eater_array[i][y * 3 + x] =
4774           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4775
4776   cav->amoeba_time              = level->amoeba_speed;
4777   cav->wonderwall_time          = level->time_magic_wall;
4778   cav->wheel_time               = level->time_wheel;
4779
4780   cav->android_move_time        = level->android_move_time;
4781   cav->android_clone_time       = level->android_clone_time;
4782   cav->ball_random              = level->ball_random;
4783   cav->ball_active              = level->ball_active_initial;
4784   cav->ball_time                = level->ball_time;
4785   cav->num_ball_arrays          = level->num_ball_contents;
4786
4787   cav->lenses_score             = level->lenses_score;
4788   cav->magnify_score            = level->magnify_score;
4789   cav->slurp_score              = level->slurp_score;
4790
4791   cav->lenses_time              = level->lenses_time;
4792   cav->magnify_time             = level->magnify_time;
4793
4794   cav->wind_time = 9999;
4795   cav->wind_direction =
4796     map_direction_RND_to_EM(level->wind_direction_initial);
4797
4798   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4799     for (j = 0; j < 8; j++)
4800       cav->ball_array[i][j] =
4801         map_element_RND_to_EM_cave(level->ball_content[i].
4802                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4803
4804   map_android_clone_elements_RND_to_EM(level);
4805
4806   // first fill the complete playfield with the empty space element
4807   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4808     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4809       cav->cave[x][y] = Cblank;
4810
4811   // then copy the real level contents from level file into the playfield
4812   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4813   {
4814     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4815
4816     if (level->field[x][y] == EL_AMOEBA_DEAD)
4817       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4818
4819     cav->cave[x][y] = new_element;
4820   }
4821
4822   for (i = 0; i < MAX_PLAYERS; i++)
4823   {
4824     cav->player_x[i] = -1;
4825     cav->player_y[i] = -1;
4826   }
4827
4828   // initialize player positions and delete players from the playfield
4829   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4830   {
4831     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4832     {
4833       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4834
4835       cav->player_x[player_nr] = x;
4836       cav->player_y[player_nr] = y;
4837
4838       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4839     }
4840   }
4841 }
4842
4843 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4844 {
4845   static int ball_xy[8][2] =
4846   {
4847     { 0, 0 },
4848     { 1, 0 },
4849     { 2, 0 },
4850     { 0, 1 },
4851     { 2, 1 },
4852     { 0, 2 },
4853     { 1, 2 },
4854     { 2, 2 },
4855   };
4856   struct LevelInfo_EM *level_em = level->native_em_level;
4857   struct CAVE *cav = level_em->cav;
4858   int i, j, x, y;
4859
4860   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4861   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4862
4863   level->time        = cav->time_seconds;
4864   level->gems_needed = cav->gems_needed;
4865
4866   sprintf(level->name, "Level %d", level->file_info.nr);
4867
4868   level->score[SC_EMERALD]      = cav->emerald_score;
4869   level->score[SC_DIAMOND]      = cav->diamond_score;
4870   level->score[SC_ROBOT]        = cav->alien_score;
4871   level->score[SC_SPACESHIP]    = cav->tank_score;
4872   level->score[SC_BUG]          = cav->bug_score;
4873   level->score[SC_YAMYAM]       = cav->eater_score;
4874   level->score[SC_NUT]          = cav->nut_score;
4875   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4876   level->score[SC_KEY]          = cav->key_score;
4877   level->score[SC_TIME_BONUS]   = cav->exit_score;
4878
4879   level->num_yamyam_contents    = cav->num_eater_arrays;
4880
4881   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4882     for (y = 0; y < 3; y++)
4883       for (x = 0; x < 3; x++)
4884         level->yamyam_content[i].e[x][y] =
4885           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4886
4887   level->amoeba_speed           = cav->amoeba_time;
4888   level->time_magic_wall        = cav->wonderwall_time;
4889   level->time_wheel             = cav->wheel_time;
4890
4891   level->android_move_time      = cav->android_move_time;
4892   level->android_clone_time     = cav->android_clone_time;
4893   level->ball_random            = cav->ball_random;
4894   level->ball_active_initial    = cav->ball_active;
4895   level->ball_time              = cav->ball_time;
4896   level->num_ball_contents      = cav->num_ball_arrays;
4897
4898   level->lenses_score           = cav->lenses_score;
4899   level->magnify_score          = cav->magnify_score;
4900   level->slurp_score            = cav->slurp_score;
4901
4902   level->lenses_time            = cav->lenses_time;
4903   level->magnify_time           = cav->magnify_time;
4904
4905   level->wind_direction_initial =
4906     map_direction_EM_to_RND(cav->wind_direction);
4907
4908   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4909     for (j = 0; j < 8; j++)
4910       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4911         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4912
4913   map_android_clone_elements_EM_to_RND(level);
4914
4915   // convert the playfield (some elements need special treatment)
4916   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4917   {
4918     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4919
4920     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4921       new_element = EL_AMOEBA_DEAD;
4922
4923     level->field[x][y] = new_element;
4924   }
4925
4926   for (i = 0; i < MAX_PLAYERS; i++)
4927   {
4928     // in case of all players set to the same field, use the first player
4929     int nr = MAX_PLAYERS - i - 1;
4930     int jx = cav->player_x[nr];
4931     int jy = cav->player_y[nr];
4932
4933     if (jx != -1 && jy != -1)
4934       level->field[jx][jy] = EL_PLAYER_1 + nr;
4935   }
4936
4937   // time score is counted for each 10 seconds left in Emerald Mine levels
4938   level->time_score_base = 10;
4939 }
4940
4941
4942 // ----------------------------------------------------------------------------
4943 // functions for loading SP level
4944 // ----------------------------------------------------------------------------
4945
4946 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4947 {
4948   struct LevelInfo_SP *level_sp = level->native_sp_level;
4949   LevelInfoType *header = &level_sp->header;
4950   int i, x, y;
4951
4952   level_sp->width  = level->fieldx;
4953   level_sp->height = level->fieldy;
4954
4955   for (x = 0; x < level->fieldx; x++)
4956     for (y = 0; y < level->fieldy; y++)
4957       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4958
4959   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4960
4961   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4962     header->LevelTitle[i] = level->name[i];
4963   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4964
4965   header->InfotronsNeeded = level->gems_needed;
4966
4967   header->SpecialPortCount = 0;
4968
4969   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4970   {
4971     boolean gravity_port_found = FALSE;
4972     boolean gravity_port_valid = FALSE;
4973     int gravity_port_flag;
4974     int gravity_port_base_element;
4975     int element = level->field[x][y];
4976
4977     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4978         element <= EL_SP_GRAVITY_ON_PORT_UP)
4979     {
4980       gravity_port_found = TRUE;
4981       gravity_port_valid = TRUE;
4982       gravity_port_flag = 1;
4983       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4984     }
4985     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4986              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4987     {
4988       gravity_port_found = TRUE;
4989       gravity_port_valid = TRUE;
4990       gravity_port_flag = 0;
4991       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4992     }
4993     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4994              element <= EL_SP_GRAVITY_PORT_UP)
4995     {
4996       // change R'n'D style gravity inverting special port to normal port
4997       // (there are no gravity inverting ports in native Supaplex engine)
4998
4999       gravity_port_found = TRUE;
5000       gravity_port_valid = FALSE;
5001       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
5002     }
5003
5004     if (gravity_port_found)
5005     {
5006       if (gravity_port_valid &&
5007           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5008       {
5009         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5010
5011         port->PortLocation = (y * level->fieldx + x) * 2;
5012         port->Gravity = gravity_port_flag;
5013
5014         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5015
5016         header->SpecialPortCount++;
5017       }
5018       else
5019       {
5020         // change special gravity port to normal port
5021
5022         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5023       }
5024
5025       level_sp->playfield[x][y] = element - EL_SP_START;
5026     }
5027   }
5028 }
5029
5030 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5031 {
5032   struct LevelInfo_SP *level_sp = level->native_sp_level;
5033   LevelInfoType *header = &level_sp->header;
5034   boolean num_invalid_elements = 0;
5035   int i, j, x, y;
5036
5037   level->fieldx = level_sp->width;
5038   level->fieldy = level_sp->height;
5039
5040   for (x = 0; x < level->fieldx; x++)
5041   {
5042     for (y = 0; y < level->fieldy; y++)
5043     {
5044       int element_old = level_sp->playfield[x][y];
5045       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5046
5047       if (element_new == EL_UNKNOWN)
5048       {
5049         num_invalid_elements++;
5050
5051         Debug("level:native:SP", "invalid element %d at position %d, %d",
5052               element_old, x, y);
5053       }
5054
5055       level->field[x][y] = element_new;
5056     }
5057   }
5058
5059   if (num_invalid_elements > 0)
5060     Warn("found %d invalid elements%s", num_invalid_elements,
5061          (!options.debug ? " (use '--debug' for more details)" : ""));
5062
5063   for (i = 0; i < MAX_PLAYERS; i++)
5064     level->initial_player_gravity[i] =
5065       (header->InitialGravity == 1 ? TRUE : FALSE);
5066
5067   // skip leading spaces
5068   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5069     if (header->LevelTitle[i] != ' ')
5070       break;
5071
5072   // copy level title
5073   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5074     level->name[j] = header->LevelTitle[i];
5075   level->name[j] = '\0';
5076
5077   // cut trailing spaces
5078   for (; j > 0; j--)
5079     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5080       level->name[j - 1] = '\0';
5081
5082   level->gems_needed = header->InfotronsNeeded;
5083
5084   for (i = 0; i < header->SpecialPortCount; i++)
5085   {
5086     SpecialPortType *port = &header->SpecialPort[i];
5087     int port_location = port->PortLocation;
5088     int gravity = port->Gravity;
5089     int port_x, port_y, port_element;
5090
5091     port_x = (port_location / 2) % level->fieldx;
5092     port_y = (port_location / 2) / level->fieldx;
5093
5094     if (port_x < 0 || port_x >= level->fieldx ||
5095         port_y < 0 || port_y >= level->fieldy)
5096     {
5097       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5098
5099       continue;
5100     }
5101
5102     port_element = level->field[port_x][port_y];
5103
5104     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5105         port_element > EL_SP_GRAVITY_PORT_UP)
5106     {
5107       Warn("no special port at position (%d, %d)", port_x, port_y);
5108
5109       continue;
5110     }
5111
5112     // change previous (wrong) gravity inverting special port to either
5113     // gravity enabling special port or gravity disabling special port
5114     level->field[port_x][port_y] +=
5115       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5116        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5117   }
5118
5119   // change special gravity ports without database entries to normal ports
5120   for (x = 0; x < level->fieldx; x++)
5121     for (y = 0; y < level->fieldy; y++)
5122       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5123           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5124         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5125
5126   level->time = 0;                      // no time limit
5127   level->amoeba_speed = 0;
5128   level->time_magic_wall = 0;
5129   level->time_wheel = 0;
5130   level->amoeba_content = EL_EMPTY;
5131
5132   // original Supaplex does not use score values -- rate by playing time
5133   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5134     level->score[i] = 0;
5135
5136   level->rate_time_over_score = TRUE;
5137
5138   // there are no yamyams in supaplex levels
5139   for (i = 0; i < level->num_yamyam_contents; i++)
5140     for (x = 0; x < 3; x++)
5141       for (y = 0; y < 3; y++)
5142         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5143 }
5144
5145 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5146 {
5147   struct LevelInfo_SP *level_sp = level->native_sp_level;
5148   struct DemoInfo_SP *demo = &level_sp->demo;
5149   int i, j;
5150
5151   // always start with reliable default values
5152   demo->is_available = FALSE;
5153   demo->length = 0;
5154
5155   if (TAPE_IS_EMPTY(tape))
5156     return;
5157
5158   demo->level_nr = tape.level_nr;       // (currently not used)
5159
5160   level_sp->header.DemoRandomSeed = tape.random_seed;
5161
5162   demo->length = 0;
5163
5164   for (i = 0; i < tape.length; i++)
5165   {
5166     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5167     int demo_repeat = tape.pos[i].delay;
5168     int demo_entries = (demo_repeat + 15) / 16;
5169
5170     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5171     {
5172       Warn("tape truncated: size exceeds maximum SP demo size %d",
5173            SP_MAX_TAPE_LEN);
5174
5175       break;
5176     }
5177
5178     for (j = 0; j < demo_repeat / 16; j++)
5179       demo->data[demo->length++] = 0xf0 | demo_action;
5180
5181     if (demo_repeat % 16)
5182       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5183   }
5184
5185   demo->is_available = TRUE;
5186 }
5187
5188 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5189 {
5190   struct LevelInfo_SP *level_sp = level->native_sp_level;
5191   struct DemoInfo_SP *demo = &level_sp->demo;
5192   char *filename = level->file_info.filename;
5193   int i;
5194
5195   // always start with reliable default values
5196   setTapeInfoToDefaults();
5197
5198   if (!demo->is_available)
5199     return;
5200
5201   tape.level_nr = demo->level_nr;       // (currently not used)
5202   tape.random_seed = level_sp->header.DemoRandomSeed;
5203
5204   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5205
5206   tape.counter = 0;
5207   tape.pos[tape.counter].delay = 0;
5208
5209   for (i = 0; i < demo->length; i++)
5210   {
5211     int demo_action = demo->data[i] & 0x0f;
5212     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5213     int tape_action = map_key_SP_to_RND(demo_action);
5214     int tape_repeat = demo_repeat + 1;
5215     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5216     boolean success = 0;
5217     int j;
5218
5219     for (j = 0; j < tape_repeat; j++)
5220       success = TapeAddAction(action);
5221
5222     if (!success)
5223     {
5224       Warn("SP demo truncated: size exceeds maximum tape size %d",
5225            MAX_TAPE_LEN);
5226
5227       break;
5228     }
5229   }
5230
5231   TapeHaltRecording();
5232 }
5233
5234
5235 // ----------------------------------------------------------------------------
5236 // functions for loading MM level
5237 // ----------------------------------------------------------------------------
5238
5239 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5240 {
5241   struct LevelInfo_MM *level_mm = level->native_mm_level;
5242   int i, x, y;
5243
5244   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5245   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5246
5247   level_mm->time = level->time;
5248   level_mm->kettles_needed = level->gems_needed;
5249   level_mm->auto_count_kettles = level->auto_count_gems;
5250
5251   level_mm->mm_laser_red   = level->mm_laser_red;
5252   level_mm->mm_laser_green = level->mm_laser_green;
5253   level_mm->mm_laser_blue  = level->mm_laser_blue;
5254
5255   level_mm->df_laser_red   = level->df_laser_red;
5256   level_mm->df_laser_green = level->df_laser_green;
5257   level_mm->df_laser_blue  = level->df_laser_blue;
5258
5259   strcpy(level_mm->name, level->name);
5260   strcpy(level_mm->author, level->author);
5261
5262   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5263   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5264   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5265   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5266   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5267
5268   level_mm->amoeba_speed = level->amoeba_speed;
5269   level_mm->time_fuse    = level->mm_time_fuse;
5270   level_mm->time_bomb    = level->mm_time_bomb;
5271   level_mm->time_ball    = level->mm_time_ball;
5272   level_mm->time_block   = level->mm_time_block;
5273
5274   level_mm->num_ball_contents = level->num_mm_ball_contents;
5275   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5276   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5277   level_mm->explode_ball = level->explode_mm_ball;
5278
5279   for (i = 0; i < level->num_mm_ball_contents; i++)
5280     level_mm->ball_content[i] =
5281       map_element_RND_to_MM(level->mm_ball_content[i]);
5282
5283   for (x = 0; x < level->fieldx; x++)
5284     for (y = 0; y < level->fieldy; y++)
5285       Ur[x][y] =
5286         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5287 }
5288
5289 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5290 {
5291   struct LevelInfo_MM *level_mm = level->native_mm_level;
5292   int i, x, y;
5293
5294   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5295   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5296
5297   level->time = level_mm->time;
5298   level->gems_needed = level_mm->kettles_needed;
5299   level->auto_count_gems = level_mm->auto_count_kettles;
5300
5301   level->mm_laser_red   = level_mm->mm_laser_red;
5302   level->mm_laser_green = level_mm->mm_laser_green;
5303   level->mm_laser_blue  = level_mm->mm_laser_blue;
5304
5305   level->df_laser_red   = level_mm->df_laser_red;
5306   level->df_laser_green = level_mm->df_laser_green;
5307   level->df_laser_blue  = level_mm->df_laser_blue;
5308
5309   strcpy(level->name, level_mm->name);
5310
5311   // only overwrite author from 'levelinfo.conf' if author defined in level
5312   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5313     strcpy(level->author, level_mm->author);
5314
5315   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5316   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5317   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5318   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5319   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5320
5321   level->amoeba_speed  = level_mm->amoeba_speed;
5322   level->mm_time_fuse  = level_mm->time_fuse;
5323   level->mm_time_bomb  = level_mm->time_bomb;
5324   level->mm_time_ball  = level_mm->time_ball;
5325   level->mm_time_block = level_mm->time_block;
5326
5327   level->num_mm_ball_contents = level_mm->num_ball_contents;
5328   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5329   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5330   level->explode_mm_ball = level_mm->explode_ball;
5331
5332   for (i = 0; i < level->num_mm_ball_contents; i++)
5333     level->mm_ball_content[i] =
5334       map_element_MM_to_RND(level_mm->ball_content[i]);
5335
5336   for (x = 0; x < level->fieldx; x++)
5337     for (y = 0; y < level->fieldy; y++)
5338       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5339 }
5340
5341
5342 // ----------------------------------------------------------------------------
5343 // functions for loading DC level
5344 // ----------------------------------------------------------------------------
5345
5346 #define DC_LEVEL_HEADER_SIZE            344
5347
5348 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5349                                         boolean init)
5350 {
5351   static int last_data_encoded;
5352   static int offset1;
5353   static int offset2;
5354   int diff;
5355   int diff_hi, diff_lo;
5356   int data_hi, data_lo;
5357   unsigned short data_decoded;
5358
5359   if (init)
5360   {
5361     last_data_encoded = 0;
5362     offset1 = -1;
5363     offset2 = 0;
5364
5365     return 0;
5366   }
5367
5368   diff = data_encoded - last_data_encoded;
5369   diff_hi = diff & ~0xff;
5370   diff_lo = diff &  0xff;
5371
5372   offset2 += diff_lo;
5373
5374   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5375   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5376   data_hi = data_hi & 0xff00;
5377
5378   data_decoded = data_hi | data_lo;
5379
5380   last_data_encoded = data_encoded;
5381
5382   offset1 = (offset1 + 1) % 31;
5383   offset2 = offset2 & 0xff;
5384
5385   return data_decoded;
5386 }
5387
5388 static int getMappedElement_DC(int element)
5389 {
5390   switch (element)
5391   {
5392     case 0x0000:
5393       element = EL_ROCK;
5394       break;
5395
5396       // 0x0117 - 0x036e: (?)
5397       // EL_DIAMOND
5398
5399       // 0x042d - 0x0684: (?)
5400       // EL_EMERALD
5401
5402     case 0x06f1:
5403       element = EL_NUT;
5404       break;
5405
5406     case 0x074c:
5407       element = EL_BOMB;
5408       break;
5409
5410     case 0x07a4:
5411       element = EL_PEARL;
5412       break;
5413
5414     case 0x0823:
5415       element = EL_CRYSTAL;
5416       break;
5417
5418     case 0x0e77:        // quicksand (boulder)
5419       element = EL_QUICKSAND_FAST_FULL;
5420       break;
5421
5422     case 0x0e99:        // slow quicksand (boulder)
5423       element = EL_QUICKSAND_FULL;
5424       break;
5425
5426     case 0x0ed2:
5427       element = EL_EM_EXIT_OPEN;
5428       break;
5429
5430     case 0x0ee3:
5431       element = EL_EM_EXIT_CLOSED;
5432       break;
5433
5434     case 0x0eeb:
5435       element = EL_EM_STEEL_EXIT_OPEN;
5436       break;
5437
5438     case 0x0efc:
5439       element = EL_EM_STEEL_EXIT_CLOSED;
5440       break;
5441
5442     case 0x0f4f:        // dynamite (lit 1)
5443       element = EL_EM_DYNAMITE_ACTIVE;
5444       break;
5445
5446     case 0x0f57:        // dynamite (lit 2)
5447       element = EL_EM_DYNAMITE_ACTIVE;
5448       break;
5449
5450     case 0x0f5f:        // dynamite (lit 3)
5451       element = EL_EM_DYNAMITE_ACTIVE;
5452       break;
5453
5454     case 0x0f67:        // dynamite (lit 4)
5455       element = EL_EM_DYNAMITE_ACTIVE;
5456       break;
5457
5458     case 0x0f81:
5459     case 0x0f82:
5460     case 0x0f83:
5461     case 0x0f84:
5462       element = EL_AMOEBA_WET;
5463       break;
5464
5465     case 0x0f85:
5466       element = EL_AMOEBA_DROP;
5467       break;
5468
5469     case 0x0fb9:
5470       element = EL_DC_MAGIC_WALL;
5471       break;
5472
5473     case 0x0fd0:
5474       element = EL_SPACESHIP_UP;
5475       break;
5476
5477     case 0x0fd9:
5478       element = EL_SPACESHIP_DOWN;
5479       break;
5480
5481     case 0x0ff1:
5482       element = EL_SPACESHIP_LEFT;
5483       break;
5484
5485     case 0x0ff9:
5486       element = EL_SPACESHIP_RIGHT;
5487       break;
5488
5489     case 0x1057:
5490       element = EL_BUG_UP;
5491       break;
5492
5493     case 0x1060:
5494       element = EL_BUG_DOWN;
5495       break;
5496
5497     case 0x1078:
5498       element = EL_BUG_LEFT;
5499       break;
5500
5501     case 0x1080:
5502       element = EL_BUG_RIGHT;
5503       break;
5504
5505     case 0x10de:
5506       element = EL_MOLE_UP;
5507       break;
5508
5509     case 0x10e7:
5510       element = EL_MOLE_DOWN;
5511       break;
5512
5513     case 0x10ff:
5514       element = EL_MOLE_LEFT;
5515       break;
5516
5517     case 0x1107:
5518       element = EL_MOLE_RIGHT;
5519       break;
5520
5521     case 0x11c0:
5522       element = EL_ROBOT;
5523       break;
5524
5525     case 0x13f5:
5526       element = EL_YAMYAM_UP;
5527       break;
5528
5529     case 0x1425:
5530       element = EL_SWITCHGATE_OPEN;
5531       break;
5532
5533     case 0x1426:
5534       element = EL_SWITCHGATE_CLOSED;
5535       break;
5536
5537     case 0x1437:
5538       element = EL_DC_SWITCHGATE_SWITCH_UP;
5539       break;
5540
5541     case 0x143a:
5542       element = EL_TIMEGATE_CLOSED;
5543       break;
5544
5545     case 0x144c:        // conveyor belt switch (green)
5546       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5547       break;
5548
5549     case 0x144f:        // conveyor belt switch (red)
5550       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5551       break;
5552
5553     case 0x1452:        // conveyor belt switch (blue)
5554       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5555       break;
5556
5557     case 0x145b:
5558       element = EL_CONVEYOR_BELT_3_MIDDLE;
5559       break;
5560
5561     case 0x1463:
5562       element = EL_CONVEYOR_BELT_3_LEFT;
5563       break;
5564
5565     case 0x146b:
5566       element = EL_CONVEYOR_BELT_3_RIGHT;
5567       break;
5568
5569     case 0x1473:
5570       element = EL_CONVEYOR_BELT_1_MIDDLE;
5571       break;
5572
5573     case 0x147b:
5574       element = EL_CONVEYOR_BELT_1_LEFT;
5575       break;
5576
5577     case 0x1483:
5578       element = EL_CONVEYOR_BELT_1_RIGHT;
5579       break;
5580
5581     case 0x148b:
5582       element = EL_CONVEYOR_BELT_4_MIDDLE;
5583       break;
5584
5585     case 0x1493:
5586       element = EL_CONVEYOR_BELT_4_LEFT;
5587       break;
5588
5589     case 0x149b:
5590       element = EL_CONVEYOR_BELT_4_RIGHT;
5591       break;
5592
5593     case 0x14ac:
5594       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5595       break;
5596
5597     case 0x14bd:
5598       element = EL_EXPANDABLE_WALL_VERTICAL;
5599       break;
5600
5601     case 0x14c6:
5602       element = EL_EXPANDABLE_WALL_ANY;
5603       break;
5604
5605     case 0x14ce:        // growing steel wall (left/right)
5606       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5607       break;
5608
5609     case 0x14df:        // growing steel wall (up/down)
5610       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5611       break;
5612
5613     case 0x14e8:        // growing steel wall (up/down/left/right)
5614       element = EL_EXPANDABLE_STEELWALL_ANY;
5615       break;
5616
5617     case 0x14e9:
5618       element = EL_SHIELD_DEADLY;
5619       break;
5620
5621     case 0x1501:
5622       element = EL_EXTRA_TIME;
5623       break;
5624
5625     case 0x154f:
5626       element = EL_ACID;
5627       break;
5628
5629     case 0x1577:
5630       element = EL_EMPTY_SPACE;
5631       break;
5632
5633     case 0x1578:        // quicksand (empty)
5634       element = EL_QUICKSAND_FAST_EMPTY;
5635       break;
5636
5637     case 0x1579:        // slow quicksand (empty)
5638       element = EL_QUICKSAND_EMPTY;
5639       break;
5640
5641       // 0x157c - 0x158b:
5642       // EL_SAND
5643
5644       // 0x1590 - 0x159f:
5645       // EL_DC_LANDMINE
5646
5647     case 0x15a0:
5648       element = EL_EM_DYNAMITE;
5649       break;
5650
5651     case 0x15a1:        // key (red)
5652       element = EL_EM_KEY_1;
5653       break;
5654
5655     case 0x15a2:        // key (yellow)
5656       element = EL_EM_KEY_2;
5657       break;
5658
5659     case 0x15a3:        // key (blue)
5660       element = EL_EM_KEY_4;
5661       break;
5662
5663     case 0x15a4:        // key (green)
5664       element = EL_EM_KEY_3;
5665       break;
5666
5667     case 0x15a5:        // key (white)
5668       element = EL_DC_KEY_WHITE;
5669       break;
5670
5671     case 0x15a6:
5672       element = EL_WALL_SLIPPERY;
5673       break;
5674
5675     case 0x15a7:
5676       element = EL_WALL;
5677       break;
5678
5679     case 0x15a8:        // wall (not round)
5680       element = EL_WALL;
5681       break;
5682
5683     case 0x15a9:        // (blue)
5684       element = EL_CHAR_A;
5685       break;
5686
5687     case 0x15aa:        // (blue)
5688       element = EL_CHAR_B;
5689       break;
5690
5691     case 0x15ab:        // (blue)
5692       element = EL_CHAR_C;
5693       break;
5694
5695     case 0x15ac:        // (blue)
5696       element = EL_CHAR_D;
5697       break;
5698
5699     case 0x15ad:        // (blue)
5700       element = EL_CHAR_E;
5701       break;
5702
5703     case 0x15ae:        // (blue)
5704       element = EL_CHAR_F;
5705       break;
5706
5707     case 0x15af:        // (blue)
5708       element = EL_CHAR_G;
5709       break;
5710
5711     case 0x15b0:        // (blue)
5712       element = EL_CHAR_H;
5713       break;
5714
5715     case 0x15b1:        // (blue)
5716       element = EL_CHAR_I;
5717       break;
5718
5719     case 0x15b2:        // (blue)
5720       element = EL_CHAR_J;
5721       break;
5722
5723     case 0x15b3:        // (blue)
5724       element = EL_CHAR_K;
5725       break;
5726
5727     case 0x15b4:        // (blue)
5728       element = EL_CHAR_L;
5729       break;
5730
5731     case 0x15b5:        // (blue)
5732       element = EL_CHAR_M;
5733       break;
5734
5735     case 0x15b6:        // (blue)
5736       element = EL_CHAR_N;
5737       break;
5738
5739     case 0x15b7:        // (blue)
5740       element = EL_CHAR_O;
5741       break;
5742
5743     case 0x15b8:        // (blue)
5744       element = EL_CHAR_P;
5745       break;
5746
5747     case 0x15b9:        // (blue)
5748       element = EL_CHAR_Q;
5749       break;
5750
5751     case 0x15ba:        // (blue)
5752       element = EL_CHAR_R;
5753       break;
5754
5755     case 0x15bb:        // (blue)
5756       element = EL_CHAR_S;
5757       break;
5758
5759     case 0x15bc:        // (blue)
5760       element = EL_CHAR_T;
5761       break;
5762
5763     case 0x15bd:        // (blue)
5764       element = EL_CHAR_U;
5765       break;
5766
5767     case 0x15be:        // (blue)
5768       element = EL_CHAR_V;
5769       break;
5770
5771     case 0x15bf:        // (blue)
5772       element = EL_CHAR_W;
5773       break;
5774
5775     case 0x15c0:        // (blue)
5776       element = EL_CHAR_X;
5777       break;
5778
5779     case 0x15c1:        // (blue)
5780       element = EL_CHAR_Y;
5781       break;
5782
5783     case 0x15c2:        // (blue)
5784       element = EL_CHAR_Z;
5785       break;
5786
5787     case 0x15c3:        // (blue)
5788       element = EL_CHAR_AUMLAUT;
5789       break;
5790
5791     case 0x15c4:        // (blue)
5792       element = EL_CHAR_OUMLAUT;
5793       break;
5794
5795     case 0x15c5:        // (blue)
5796       element = EL_CHAR_UUMLAUT;
5797       break;
5798
5799     case 0x15c6:        // (blue)
5800       element = EL_CHAR_0;
5801       break;
5802
5803     case 0x15c7:        // (blue)
5804       element = EL_CHAR_1;
5805       break;
5806
5807     case 0x15c8:        // (blue)
5808       element = EL_CHAR_2;
5809       break;
5810
5811     case 0x15c9:        // (blue)
5812       element = EL_CHAR_3;
5813       break;
5814
5815     case 0x15ca:        // (blue)
5816       element = EL_CHAR_4;
5817       break;
5818
5819     case 0x15cb:        // (blue)
5820       element = EL_CHAR_5;
5821       break;
5822
5823     case 0x15cc:        // (blue)
5824       element = EL_CHAR_6;
5825       break;
5826
5827     case 0x15cd:        // (blue)
5828       element = EL_CHAR_7;
5829       break;
5830
5831     case 0x15ce:        // (blue)
5832       element = EL_CHAR_8;
5833       break;
5834
5835     case 0x15cf:        // (blue)
5836       element = EL_CHAR_9;
5837       break;
5838
5839     case 0x15d0:        // (blue)
5840       element = EL_CHAR_PERIOD;
5841       break;
5842
5843     case 0x15d1:        // (blue)
5844       element = EL_CHAR_EXCLAM;
5845       break;
5846
5847     case 0x15d2:        // (blue)
5848       element = EL_CHAR_COLON;
5849       break;
5850
5851     case 0x15d3:        // (blue)
5852       element = EL_CHAR_LESS;
5853       break;
5854
5855     case 0x15d4:        // (blue)
5856       element = EL_CHAR_GREATER;
5857       break;
5858
5859     case 0x15d5:        // (blue)
5860       element = EL_CHAR_QUESTION;
5861       break;
5862
5863     case 0x15d6:        // (blue)
5864       element = EL_CHAR_COPYRIGHT;
5865       break;
5866
5867     case 0x15d7:        // (blue)
5868       element = EL_CHAR_UP;
5869       break;
5870
5871     case 0x15d8:        // (blue)
5872       element = EL_CHAR_DOWN;
5873       break;
5874
5875     case 0x15d9:        // (blue)
5876       element = EL_CHAR_BUTTON;
5877       break;
5878
5879     case 0x15da:        // (blue)
5880       element = EL_CHAR_PLUS;
5881       break;
5882
5883     case 0x15db:        // (blue)
5884       element = EL_CHAR_MINUS;
5885       break;
5886
5887     case 0x15dc:        // (blue)
5888       element = EL_CHAR_APOSTROPHE;
5889       break;
5890
5891     case 0x15dd:        // (blue)
5892       element = EL_CHAR_PARENLEFT;
5893       break;
5894
5895     case 0x15de:        // (blue)
5896       element = EL_CHAR_PARENRIGHT;
5897       break;
5898
5899     case 0x15df:        // (green)
5900       element = EL_CHAR_A;
5901       break;
5902
5903     case 0x15e0:        // (green)
5904       element = EL_CHAR_B;
5905       break;
5906
5907     case 0x15e1:        // (green)
5908       element = EL_CHAR_C;
5909       break;
5910
5911     case 0x15e2:        // (green)
5912       element = EL_CHAR_D;
5913       break;
5914
5915     case 0x15e3:        // (green)
5916       element = EL_CHAR_E;
5917       break;
5918
5919     case 0x15e4:        // (green)
5920       element = EL_CHAR_F;
5921       break;
5922
5923     case 0x15e5:        // (green)
5924       element = EL_CHAR_G;
5925       break;
5926
5927     case 0x15e6:        // (green)
5928       element = EL_CHAR_H;
5929       break;
5930
5931     case 0x15e7:        // (green)
5932       element = EL_CHAR_I;
5933       break;
5934
5935     case 0x15e8:        // (green)
5936       element = EL_CHAR_J;
5937       break;
5938
5939     case 0x15e9:        // (green)
5940       element = EL_CHAR_K;
5941       break;
5942
5943     case 0x15ea:        // (green)
5944       element = EL_CHAR_L;
5945       break;
5946
5947     case 0x15eb:        // (green)
5948       element = EL_CHAR_M;
5949       break;
5950
5951     case 0x15ec:        // (green)
5952       element = EL_CHAR_N;
5953       break;
5954
5955     case 0x15ed:        // (green)
5956       element = EL_CHAR_O;
5957       break;
5958
5959     case 0x15ee:        // (green)
5960       element = EL_CHAR_P;
5961       break;
5962
5963     case 0x15ef:        // (green)
5964       element = EL_CHAR_Q;
5965       break;
5966
5967     case 0x15f0:        // (green)
5968       element = EL_CHAR_R;
5969       break;
5970
5971     case 0x15f1:        // (green)
5972       element = EL_CHAR_S;
5973       break;
5974
5975     case 0x15f2:        // (green)
5976       element = EL_CHAR_T;
5977       break;
5978
5979     case 0x15f3:        // (green)
5980       element = EL_CHAR_U;
5981       break;
5982
5983     case 0x15f4:        // (green)
5984       element = EL_CHAR_V;
5985       break;
5986
5987     case 0x15f5:        // (green)
5988       element = EL_CHAR_W;
5989       break;
5990
5991     case 0x15f6:        // (green)
5992       element = EL_CHAR_X;
5993       break;
5994
5995     case 0x15f7:        // (green)
5996       element = EL_CHAR_Y;
5997       break;
5998
5999     case 0x15f8:        // (green)
6000       element = EL_CHAR_Z;
6001       break;
6002
6003     case 0x15f9:        // (green)
6004       element = EL_CHAR_AUMLAUT;
6005       break;
6006
6007     case 0x15fa:        // (green)
6008       element = EL_CHAR_OUMLAUT;
6009       break;
6010
6011     case 0x15fb:        // (green)
6012       element = EL_CHAR_UUMLAUT;
6013       break;
6014
6015     case 0x15fc:        // (green)
6016       element = EL_CHAR_0;
6017       break;
6018
6019     case 0x15fd:        // (green)
6020       element = EL_CHAR_1;
6021       break;
6022
6023     case 0x15fe:        // (green)
6024       element = EL_CHAR_2;
6025       break;
6026
6027     case 0x15ff:        // (green)
6028       element = EL_CHAR_3;
6029       break;
6030
6031     case 0x1600:        // (green)
6032       element = EL_CHAR_4;
6033       break;
6034
6035     case 0x1601:        // (green)
6036       element = EL_CHAR_5;
6037       break;
6038
6039     case 0x1602:        // (green)
6040       element = EL_CHAR_6;
6041       break;
6042
6043     case 0x1603:        // (green)
6044       element = EL_CHAR_7;
6045       break;
6046
6047     case 0x1604:        // (green)
6048       element = EL_CHAR_8;
6049       break;
6050
6051     case 0x1605:        // (green)
6052       element = EL_CHAR_9;
6053       break;
6054
6055     case 0x1606:        // (green)
6056       element = EL_CHAR_PERIOD;
6057       break;
6058
6059     case 0x1607:        // (green)
6060       element = EL_CHAR_EXCLAM;
6061       break;
6062
6063     case 0x1608:        // (green)
6064       element = EL_CHAR_COLON;
6065       break;
6066
6067     case 0x1609:        // (green)
6068       element = EL_CHAR_LESS;
6069       break;
6070
6071     case 0x160a:        // (green)
6072       element = EL_CHAR_GREATER;
6073       break;
6074
6075     case 0x160b:        // (green)
6076       element = EL_CHAR_QUESTION;
6077       break;
6078
6079     case 0x160c:        // (green)
6080       element = EL_CHAR_COPYRIGHT;
6081       break;
6082
6083     case 0x160d:        // (green)
6084       element = EL_CHAR_UP;
6085       break;
6086
6087     case 0x160e:        // (green)
6088       element = EL_CHAR_DOWN;
6089       break;
6090
6091     case 0x160f:        // (green)
6092       element = EL_CHAR_BUTTON;
6093       break;
6094
6095     case 0x1610:        // (green)
6096       element = EL_CHAR_PLUS;
6097       break;
6098
6099     case 0x1611:        // (green)
6100       element = EL_CHAR_MINUS;
6101       break;
6102
6103     case 0x1612:        // (green)
6104       element = EL_CHAR_APOSTROPHE;
6105       break;
6106
6107     case 0x1613:        // (green)
6108       element = EL_CHAR_PARENLEFT;
6109       break;
6110
6111     case 0x1614:        // (green)
6112       element = EL_CHAR_PARENRIGHT;
6113       break;
6114
6115     case 0x1615:        // (blue steel)
6116       element = EL_STEEL_CHAR_A;
6117       break;
6118
6119     case 0x1616:        // (blue steel)
6120       element = EL_STEEL_CHAR_B;
6121       break;
6122
6123     case 0x1617:        // (blue steel)
6124       element = EL_STEEL_CHAR_C;
6125       break;
6126
6127     case 0x1618:        // (blue steel)
6128       element = EL_STEEL_CHAR_D;
6129       break;
6130
6131     case 0x1619:        // (blue steel)
6132       element = EL_STEEL_CHAR_E;
6133       break;
6134
6135     case 0x161a:        // (blue steel)
6136       element = EL_STEEL_CHAR_F;
6137       break;
6138
6139     case 0x161b:        // (blue steel)
6140       element = EL_STEEL_CHAR_G;
6141       break;
6142
6143     case 0x161c:        // (blue steel)
6144       element = EL_STEEL_CHAR_H;
6145       break;
6146
6147     case 0x161d:        // (blue steel)
6148       element = EL_STEEL_CHAR_I;
6149       break;
6150
6151     case 0x161e:        // (blue steel)
6152       element = EL_STEEL_CHAR_J;
6153       break;
6154
6155     case 0x161f:        // (blue steel)
6156       element = EL_STEEL_CHAR_K;
6157       break;
6158
6159     case 0x1620:        // (blue steel)
6160       element = EL_STEEL_CHAR_L;
6161       break;
6162
6163     case 0x1621:        // (blue steel)
6164       element = EL_STEEL_CHAR_M;
6165       break;
6166
6167     case 0x1622:        // (blue steel)
6168       element = EL_STEEL_CHAR_N;
6169       break;
6170
6171     case 0x1623:        // (blue steel)
6172       element = EL_STEEL_CHAR_O;
6173       break;
6174
6175     case 0x1624:        // (blue steel)
6176       element = EL_STEEL_CHAR_P;
6177       break;
6178
6179     case 0x1625:        // (blue steel)
6180       element = EL_STEEL_CHAR_Q;
6181       break;
6182
6183     case 0x1626:        // (blue steel)
6184       element = EL_STEEL_CHAR_R;
6185       break;
6186
6187     case 0x1627:        // (blue steel)
6188       element = EL_STEEL_CHAR_S;
6189       break;
6190
6191     case 0x1628:        // (blue steel)
6192       element = EL_STEEL_CHAR_T;
6193       break;
6194
6195     case 0x1629:        // (blue steel)
6196       element = EL_STEEL_CHAR_U;
6197       break;
6198
6199     case 0x162a:        // (blue steel)
6200       element = EL_STEEL_CHAR_V;
6201       break;
6202
6203     case 0x162b:        // (blue steel)
6204       element = EL_STEEL_CHAR_W;
6205       break;
6206
6207     case 0x162c:        // (blue steel)
6208       element = EL_STEEL_CHAR_X;
6209       break;
6210
6211     case 0x162d:        // (blue steel)
6212       element = EL_STEEL_CHAR_Y;
6213       break;
6214
6215     case 0x162e:        // (blue steel)
6216       element = EL_STEEL_CHAR_Z;
6217       break;
6218
6219     case 0x162f:        // (blue steel)
6220       element = EL_STEEL_CHAR_AUMLAUT;
6221       break;
6222
6223     case 0x1630:        // (blue steel)
6224       element = EL_STEEL_CHAR_OUMLAUT;
6225       break;
6226
6227     case 0x1631:        // (blue steel)
6228       element = EL_STEEL_CHAR_UUMLAUT;
6229       break;
6230
6231     case 0x1632:        // (blue steel)
6232       element = EL_STEEL_CHAR_0;
6233       break;
6234
6235     case 0x1633:        // (blue steel)
6236       element = EL_STEEL_CHAR_1;
6237       break;
6238
6239     case 0x1634:        // (blue steel)
6240       element = EL_STEEL_CHAR_2;
6241       break;
6242
6243     case 0x1635:        // (blue steel)
6244       element = EL_STEEL_CHAR_3;
6245       break;
6246
6247     case 0x1636:        // (blue steel)
6248       element = EL_STEEL_CHAR_4;
6249       break;
6250
6251     case 0x1637:        // (blue steel)
6252       element = EL_STEEL_CHAR_5;
6253       break;
6254
6255     case 0x1638:        // (blue steel)
6256       element = EL_STEEL_CHAR_6;
6257       break;
6258
6259     case 0x1639:        // (blue steel)
6260       element = EL_STEEL_CHAR_7;
6261       break;
6262
6263     case 0x163a:        // (blue steel)
6264       element = EL_STEEL_CHAR_8;
6265       break;
6266
6267     case 0x163b:        // (blue steel)
6268       element = EL_STEEL_CHAR_9;
6269       break;
6270
6271     case 0x163c:        // (blue steel)
6272       element = EL_STEEL_CHAR_PERIOD;
6273       break;
6274
6275     case 0x163d:        // (blue steel)
6276       element = EL_STEEL_CHAR_EXCLAM;
6277       break;
6278
6279     case 0x163e:        // (blue steel)
6280       element = EL_STEEL_CHAR_COLON;
6281       break;
6282
6283     case 0x163f:        // (blue steel)
6284       element = EL_STEEL_CHAR_LESS;
6285       break;
6286
6287     case 0x1640:        // (blue steel)
6288       element = EL_STEEL_CHAR_GREATER;
6289       break;
6290
6291     case 0x1641:        // (blue steel)
6292       element = EL_STEEL_CHAR_QUESTION;
6293       break;
6294
6295     case 0x1642:        // (blue steel)
6296       element = EL_STEEL_CHAR_COPYRIGHT;
6297       break;
6298
6299     case 0x1643:        // (blue steel)
6300       element = EL_STEEL_CHAR_UP;
6301       break;
6302
6303     case 0x1644:        // (blue steel)
6304       element = EL_STEEL_CHAR_DOWN;
6305       break;
6306
6307     case 0x1645:        // (blue steel)
6308       element = EL_STEEL_CHAR_BUTTON;
6309       break;
6310
6311     case 0x1646:        // (blue steel)
6312       element = EL_STEEL_CHAR_PLUS;
6313       break;
6314
6315     case 0x1647:        // (blue steel)
6316       element = EL_STEEL_CHAR_MINUS;
6317       break;
6318
6319     case 0x1648:        // (blue steel)
6320       element = EL_STEEL_CHAR_APOSTROPHE;
6321       break;
6322
6323     case 0x1649:        // (blue steel)
6324       element = EL_STEEL_CHAR_PARENLEFT;
6325       break;
6326
6327     case 0x164a:        // (blue steel)
6328       element = EL_STEEL_CHAR_PARENRIGHT;
6329       break;
6330
6331     case 0x164b:        // (green steel)
6332       element = EL_STEEL_CHAR_A;
6333       break;
6334
6335     case 0x164c:        // (green steel)
6336       element = EL_STEEL_CHAR_B;
6337       break;
6338
6339     case 0x164d:        // (green steel)
6340       element = EL_STEEL_CHAR_C;
6341       break;
6342
6343     case 0x164e:        // (green steel)
6344       element = EL_STEEL_CHAR_D;
6345       break;
6346
6347     case 0x164f:        // (green steel)
6348       element = EL_STEEL_CHAR_E;
6349       break;
6350
6351     case 0x1650:        // (green steel)
6352       element = EL_STEEL_CHAR_F;
6353       break;
6354
6355     case 0x1651:        // (green steel)
6356       element = EL_STEEL_CHAR_G;
6357       break;
6358
6359     case 0x1652:        // (green steel)
6360       element = EL_STEEL_CHAR_H;
6361       break;
6362
6363     case 0x1653:        // (green steel)
6364       element = EL_STEEL_CHAR_I;
6365       break;
6366
6367     case 0x1654:        // (green steel)
6368       element = EL_STEEL_CHAR_J;
6369       break;
6370
6371     case 0x1655:        // (green steel)
6372       element = EL_STEEL_CHAR_K;
6373       break;
6374
6375     case 0x1656:        // (green steel)
6376       element = EL_STEEL_CHAR_L;
6377       break;
6378
6379     case 0x1657:        // (green steel)
6380       element = EL_STEEL_CHAR_M;
6381       break;
6382
6383     case 0x1658:        // (green steel)
6384       element = EL_STEEL_CHAR_N;
6385       break;
6386
6387     case 0x1659:        // (green steel)
6388       element = EL_STEEL_CHAR_O;
6389       break;
6390
6391     case 0x165a:        // (green steel)
6392       element = EL_STEEL_CHAR_P;
6393       break;
6394
6395     case 0x165b:        // (green steel)
6396       element = EL_STEEL_CHAR_Q;
6397       break;
6398
6399     case 0x165c:        // (green steel)
6400       element = EL_STEEL_CHAR_R;
6401       break;
6402
6403     case 0x165d:        // (green steel)
6404       element = EL_STEEL_CHAR_S;
6405       break;
6406
6407     case 0x165e:        // (green steel)
6408       element = EL_STEEL_CHAR_T;
6409       break;
6410
6411     case 0x165f:        // (green steel)
6412       element = EL_STEEL_CHAR_U;
6413       break;
6414
6415     case 0x1660:        // (green steel)
6416       element = EL_STEEL_CHAR_V;
6417       break;
6418
6419     case 0x1661:        // (green steel)
6420       element = EL_STEEL_CHAR_W;
6421       break;
6422
6423     case 0x1662:        // (green steel)
6424       element = EL_STEEL_CHAR_X;
6425       break;
6426
6427     case 0x1663:        // (green steel)
6428       element = EL_STEEL_CHAR_Y;
6429       break;
6430
6431     case 0x1664:        // (green steel)
6432       element = EL_STEEL_CHAR_Z;
6433       break;
6434
6435     case 0x1665:        // (green steel)
6436       element = EL_STEEL_CHAR_AUMLAUT;
6437       break;
6438
6439     case 0x1666:        // (green steel)
6440       element = EL_STEEL_CHAR_OUMLAUT;
6441       break;
6442
6443     case 0x1667:        // (green steel)
6444       element = EL_STEEL_CHAR_UUMLAUT;
6445       break;
6446
6447     case 0x1668:        // (green steel)
6448       element = EL_STEEL_CHAR_0;
6449       break;
6450
6451     case 0x1669:        // (green steel)
6452       element = EL_STEEL_CHAR_1;
6453       break;
6454
6455     case 0x166a:        // (green steel)
6456       element = EL_STEEL_CHAR_2;
6457       break;
6458
6459     case 0x166b:        // (green steel)
6460       element = EL_STEEL_CHAR_3;
6461       break;
6462
6463     case 0x166c:        // (green steel)
6464       element = EL_STEEL_CHAR_4;
6465       break;
6466
6467     case 0x166d:        // (green steel)
6468       element = EL_STEEL_CHAR_5;
6469       break;
6470
6471     case 0x166e:        // (green steel)
6472       element = EL_STEEL_CHAR_6;
6473       break;
6474
6475     case 0x166f:        // (green steel)
6476       element = EL_STEEL_CHAR_7;
6477       break;
6478
6479     case 0x1670:        // (green steel)
6480       element = EL_STEEL_CHAR_8;
6481       break;
6482
6483     case 0x1671:        // (green steel)
6484       element = EL_STEEL_CHAR_9;
6485       break;
6486
6487     case 0x1672:        // (green steel)
6488       element = EL_STEEL_CHAR_PERIOD;
6489       break;
6490
6491     case 0x1673:        // (green steel)
6492       element = EL_STEEL_CHAR_EXCLAM;
6493       break;
6494
6495     case 0x1674:        // (green steel)
6496       element = EL_STEEL_CHAR_COLON;
6497       break;
6498
6499     case 0x1675:        // (green steel)
6500       element = EL_STEEL_CHAR_LESS;
6501       break;
6502
6503     case 0x1676:        // (green steel)
6504       element = EL_STEEL_CHAR_GREATER;
6505       break;
6506
6507     case 0x1677:        // (green steel)
6508       element = EL_STEEL_CHAR_QUESTION;
6509       break;
6510
6511     case 0x1678:        // (green steel)
6512       element = EL_STEEL_CHAR_COPYRIGHT;
6513       break;
6514
6515     case 0x1679:        // (green steel)
6516       element = EL_STEEL_CHAR_UP;
6517       break;
6518
6519     case 0x167a:        // (green steel)
6520       element = EL_STEEL_CHAR_DOWN;
6521       break;
6522
6523     case 0x167b:        // (green steel)
6524       element = EL_STEEL_CHAR_BUTTON;
6525       break;
6526
6527     case 0x167c:        // (green steel)
6528       element = EL_STEEL_CHAR_PLUS;
6529       break;
6530
6531     case 0x167d:        // (green steel)
6532       element = EL_STEEL_CHAR_MINUS;
6533       break;
6534
6535     case 0x167e:        // (green steel)
6536       element = EL_STEEL_CHAR_APOSTROPHE;
6537       break;
6538
6539     case 0x167f:        // (green steel)
6540       element = EL_STEEL_CHAR_PARENLEFT;
6541       break;
6542
6543     case 0x1680:        // (green steel)
6544       element = EL_STEEL_CHAR_PARENRIGHT;
6545       break;
6546
6547     case 0x1681:        // gate (red)
6548       element = EL_EM_GATE_1;
6549       break;
6550
6551     case 0x1682:        // secret gate (red)
6552       element = EL_EM_GATE_1_GRAY;
6553       break;
6554
6555     case 0x1683:        // gate (yellow)
6556       element = EL_EM_GATE_2;
6557       break;
6558
6559     case 0x1684:        // secret gate (yellow)
6560       element = EL_EM_GATE_2_GRAY;
6561       break;
6562
6563     case 0x1685:        // gate (blue)
6564       element = EL_EM_GATE_4;
6565       break;
6566
6567     case 0x1686:        // secret gate (blue)
6568       element = EL_EM_GATE_4_GRAY;
6569       break;
6570
6571     case 0x1687:        // gate (green)
6572       element = EL_EM_GATE_3;
6573       break;
6574
6575     case 0x1688:        // secret gate (green)
6576       element = EL_EM_GATE_3_GRAY;
6577       break;
6578
6579     case 0x1689:        // gate (white)
6580       element = EL_DC_GATE_WHITE;
6581       break;
6582
6583     case 0x168a:        // secret gate (white)
6584       element = EL_DC_GATE_WHITE_GRAY;
6585       break;
6586
6587     case 0x168b:        // secret gate (no key)
6588       element = EL_DC_GATE_FAKE_GRAY;
6589       break;
6590
6591     case 0x168c:
6592       element = EL_ROBOT_WHEEL;
6593       break;
6594
6595     case 0x168d:
6596       element = EL_DC_TIMEGATE_SWITCH;
6597       break;
6598
6599     case 0x168e:
6600       element = EL_ACID_POOL_BOTTOM;
6601       break;
6602
6603     case 0x168f:
6604       element = EL_ACID_POOL_TOPLEFT;
6605       break;
6606
6607     case 0x1690:
6608       element = EL_ACID_POOL_TOPRIGHT;
6609       break;
6610
6611     case 0x1691:
6612       element = EL_ACID_POOL_BOTTOMLEFT;
6613       break;
6614
6615     case 0x1692:
6616       element = EL_ACID_POOL_BOTTOMRIGHT;
6617       break;
6618
6619     case 0x1693:
6620       element = EL_STEELWALL;
6621       break;
6622
6623     case 0x1694:
6624       element = EL_STEELWALL_SLIPPERY;
6625       break;
6626
6627     case 0x1695:        // steel wall (not round)
6628       element = EL_STEELWALL;
6629       break;
6630
6631     case 0x1696:        // steel wall (left)
6632       element = EL_DC_STEELWALL_1_LEFT;
6633       break;
6634
6635     case 0x1697:        // steel wall (bottom)
6636       element = EL_DC_STEELWALL_1_BOTTOM;
6637       break;
6638
6639     case 0x1698:        // steel wall (right)
6640       element = EL_DC_STEELWALL_1_RIGHT;
6641       break;
6642
6643     case 0x1699:        // steel wall (top)
6644       element = EL_DC_STEELWALL_1_TOP;
6645       break;
6646
6647     case 0x169a:        // steel wall (left/bottom)
6648       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6649       break;
6650
6651     case 0x169b:        // steel wall (right/bottom)
6652       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6653       break;
6654
6655     case 0x169c:        // steel wall (right/top)
6656       element = EL_DC_STEELWALL_1_TOPRIGHT;
6657       break;
6658
6659     case 0x169d:        // steel wall (left/top)
6660       element = EL_DC_STEELWALL_1_TOPLEFT;
6661       break;
6662
6663     case 0x169e:        // steel wall (right/bottom small)
6664       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6665       break;
6666
6667     case 0x169f:        // steel wall (left/bottom small)
6668       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6669       break;
6670
6671     case 0x16a0:        // steel wall (right/top small)
6672       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6673       break;
6674
6675     case 0x16a1:        // steel wall (left/top small)
6676       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6677       break;
6678
6679     case 0x16a2:        // steel wall (left/right)
6680       element = EL_DC_STEELWALL_1_VERTICAL;
6681       break;
6682
6683     case 0x16a3:        // steel wall (top/bottom)
6684       element = EL_DC_STEELWALL_1_HORIZONTAL;
6685       break;
6686
6687     case 0x16a4:        // steel wall 2 (left end)
6688       element = EL_DC_STEELWALL_2_LEFT;
6689       break;
6690
6691     case 0x16a5:        // steel wall 2 (right end)
6692       element = EL_DC_STEELWALL_2_RIGHT;
6693       break;
6694
6695     case 0x16a6:        // steel wall 2 (top end)
6696       element = EL_DC_STEELWALL_2_TOP;
6697       break;
6698
6699     case 0x16a7:        // steel wall 2 (bottom end)
6700       element = EL_DC_STEELWALL_2_BOTTOM;
6701       break;
6702
6703     case 0x16a8:        // steel wall 2 (left/right)
6704       element = EL_DC_STEELWALL_2_HORIZONTAL;
6705       break;
6706
6707     case 0x16a9:        // steel wall 2 (up/down)
6708       element = EL_DC_STEELWALL_2_VERTICAL;
6709       break;
6710
6711     case 0x16aa:        // steel wall 2 (mid)
6712       element = EL_DC_STEELWALL_2_MIDDLE;
6713       break;
6714
6715     case 0x16ab:
6716       element = EL_SIGN_EXCLAMATION;
6717       break;
6718
6719     case 0x16ac:
6720       element = EL_SIGN_RADIOACTIVITY;
6721       break;
6722
6723     case 0x16ad:
6724       element = EL_SIGN_STOP;
6725       break;
6726
6727     case 0x16ae:
6728       element = EL_SIGN_WHEELCHAIR;
6729       break;
6730
6731     case 0x16af:
6732       element = EL_SIGN_PARKING;
6733       break;
6734
6735     case 0x16b0:
6736       element = EL_SIGN_NO_ENTRY;
6737       break;
6738
6739     case 0x16b1:
6740       element = EL_SIGN_HEART;
6741       break;
6742
6743     case 0x16b2:
6744       element = EL_SIGN_GIVE_WAY;
6745       break;
6746
6747     case 0x16b3:
6748       element = EL_SIGN_ENTRY_FORBIDDEN;
6749       break;
6750
6751     case 0x16b4:
6752       element = EL_SIGN_EMERGENCY_EXIT;
6753       break;
6754
6755     case 0x16b5:
6756       element = EL_SIGN_YIN_YANG;
6757       break;
6758
6759     case 0x16b6:
6760       element = EL_WALL_EMERALD;
6761       break;
6762
6763     case 0x16b7:
6764       element = EL_WALL_DIAMOND;
6765       break;
6766
6767     case 0x16b8:
6768       element = EL_WALL_PEARL;
6769       break;
6770
6771     case 0x16b9:
6772       element = EL_WALL_CRYSTAL;
6773       break;
6774
6775     case 0x16ba:
6776       element = EL_INVISIBLE_WALL;
6777       break;
6778
6779     case 0x16bb:
6780       element = EL_INVISIBLE_STEELWALL;
6781       break;
6782
6783       // 0x16bc - 0x16cb:
6784       // EL_INVISIBLE_SAND
6785
6786     case 0x16cc:
6787       element = EL_LIGHT_SWITCH;
6788       break;
6789
6790     case 0x16cd:
6791       element = EL_ENVELOPE_1;
6792       break;
6793
6794     default:
6795       if (element >= 0x0117 && element <= 0x036e)       // (?)
6796         element = EL_DIAMOND;
6797       else if (element >= 0x042d && element <= 0x0684)  // (?)
6798         element = EL_EMERALD;
6799       else if (element >= 0x157c && element <= 0x158b)
6800         element = EL_SAND;
6801       else if (element >= 0x1590 && element <= 0x159f)
6802         element = EL_DC_LANDMINE;
6803       else if (element >= 0x16bc && element <= 0x16cb)
6804         element = EL_INVISIBLE_SAND;
6805       else
6806       {
6807         Warn("unknown Diamond Caves element 0x%04x", element);
6808
6809         element = EL_UNKNOWN;
6810       }
6811       break;
6812   }
6813
6814   return getMappedElement(element);
6815 }
6816
6817 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6818 {
6819   byte header[DC_LEVEL_HEADER_SIZE];
6820   int envelope_size;
6821   int envelope_header_pos = 62;
6822   int envelope_content_pos = 94;
6823   int level_name_pos = 251;
6824   int level_author_pos = 292;
6825   int envelope_header_len;
6826   int envelope_content_len;
6827   int level_name_len;
6828   int level_author_len;
6829   int fieldx, fieldy;
6830   int num_yamyam_contents;
6831   int i, x, y;
6832
6833   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6834
6835   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6836   {
6837     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6838
6839     header[i * 2 + 0] = header_word >> 8;
6840     header[i * 2 + 1] = header_word & 0xff;
6841   }
6842
6843   // read some values from level header to check level decoding integrity
6844   fieldx = header[6] | (header[7] << 8);
6845   fieldy = header[8] | (header[9] << 8);
6846   num_yamyam_contents = header[60] | (header[61] << 8);
6847
6848   // do some simple sanity checks to ensure that level was correctly decoded
6849   if (fieldx < 1 || fieldx > 256 ||
6850       fieldy < 1 || fieldy > 256 ||
6851       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6852   {
6853     level->no_valid_file = TRUE;
6854
6855     Warn("cannot decode level from stream -- using empty level");
6856
6857     return;
6858   }
6859
6860   // maximum envelope header size is 31 bytes
6861   envelope_header_len   = header[envelope_header_pos];
6862   // maximum envelope content size is 110 (156?) bytes
6863   envelope_content_len  = header[envelope_content_pos];
6864
6865   // maximum level title size is 40 bytes
6866   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6867   // maximum level author size is 30 (51?) bytes
6868   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6869
6870   envelope_size = 0;
6871
6872   for (i = 0; i < envelope_header_len; i++)
6873     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6874       level->envelope[0].text[envelope_size++] =
6875         header[envelope_header_pos + 1 + i];
6876
6877   if (envelope_header_len > 0 && envelope_content_len > 0)
6878   {
6879     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6880       level->envelope[0].text[envelope_size++] = '\n';
6881     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6882       level->envelope[0].text[envelope_size++] = '\n';
6883   }
6884
6885   for (i = 0; i < envelope_content_len; i++)
6886     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6887       level->envelope[0].text[envelope_size++] =
6888         header[envelope_content_pos + 1 + i];
6889
6890   level->envelope[0].text[envelope_size] = '\0';
6891
6892   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6893   level->envelope[0].ysize = 10;
6894   level->envelope[0].autowrap = TRUE;
6895   level->envelope[0].centered = TRUE;
6896
6897   for (i = 0; i < level_name_len; i++)
6898     level->name[i] = header[level_name_pos + 1 + i];
6899   level->name[level_name_len] = '\0';
6900
6901   for (i = 0; i < level_author_len; i++)
6902     level->author[i] = header[level_author_pos + 1 + i];
6903   level->author[level_author_len] = '\0';
6904
6905   num_yamyam_contents = header[60] | (header[61] << 8);
6906   level->num_yamyam_contents =
6907     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6908
6909   for (i = 0; i < num_yamyam_contents; i++)
6910   {
6911     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6912     {
6913       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6914       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6915
6916       if (i < MAX_ELEMENT_CONTENTS)
6917         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6918     }
6919   }
6920
6921   fieldx = header[6] | (header[7] << 8);
6922   fieldy = header[8] | (header[9] << 8);
6923   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6924   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6925
6926   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6927   {
6928     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6929     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6930
6931     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6932       level->field[x][y] = getMappedElement_DC(element_dc);
6933   }
6934
6935   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6936   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6937   level->field[x][y] = EL_PLAYER_1;
6938
6939   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6940   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6941   level->field[x][y] = EL_PLAYER_2;
6942
6943   level->gems_needed            = header[18] | (header[19] << 8);
6944
6945   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6946   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6947   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6948   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6949   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6950   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6951   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6952   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6953   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6954   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6955   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6956   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6957
6958   level->time                   = header[44] | (header[45] << 8);
6959
6960   level->amoeba_speed           = header[46] | (header[47] << 8);
6961   level->time_light             = header[48] | (header[49] << 8);
6962   level->time_timegate          = header[50] | (header[51] << 8);
6963   level->time_wheel             = header[52] | (header[53] << 8);
6964   level->time_magic_wall        = header[54] | (header[55] << 8);
6965   level->extra_time             = header[56] | (header[57] << 8);
6966   level->shield_normal_time     = header[58] | (header[59] << 8);
6967
6968   // shield and extra time elements do not have a score
6969   level->score[SC_SHIELD]       = 0;
6970   level->extra_time_score       = 0;
6971
6972   // set time for normal and deadly shields to the same value
6973   level->shield_deadly_time     = level->shield_normal_time;
6974
6975   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6976   // can slip down from flat walls, like normal walls and steel walls
6977   level->em_slippery_gems = TRUE;
6978
6979   // time score is counted for each 10 seconds left in Diamond Caves levels
6980   level->time_score_base = 10;
6981 }
6982
6983 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6984                                      struct LevelFileInfo *level_file_info,
6985                                      boolean level_info_only)
6986 {
6987   char *filename = level_file_info->filename;
6988   File *file;
6989   int num_magic_bytes = 8;
6990   char magic_bytes[num_magic_bytes + 1];
6991   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6992
6993   if (!(file = openFile(filename, MODE_READ)))
6994   {
6995     level->no_valid_file = TRUE;
6996
6997     if (!level_info_only)
6998       Warn("cannot read level '%s' -- using empty level", filename);
6999
7000     return;
7001   }
7002
7003   // fseek(file, 0x0000, SEEK_SET);
7004
7005   if (level_file_info->packed)
7006   {
7007     // read "magic bytes" from start of file
7008     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7009       magic_bytes[0] = '\0';
7010
7011     // check "magic bytes" for correct file format
7012     if (!strPrefix(magic_bytes, "DC2"))
7013     {
7014       level->no_valid_file = TRUE;
7015
7016       Warn("unknown DC level file '%s' -- using empty level", filename);
7017
7018       return;
7019     }
7020
7021     if (strPrefix(magic_bytes, "DC2Win95") ||
7022         strPrefix(magic_bytes, "DC2Win98"))
7023     {
7024       int position_first_level = 0x00fa;
7025       int extra_bytes = 4;
7026       int skip_bytes;
7027
7028       // advance file stream to first level inside the level package
7029       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7030
7031       // each block of level data is followed by block of non-level data
7032       num_levels_to_skip *= 2;
7033
7034       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7035       while (num_levels_to_skip >= 0)
7036       {
7037         // advance file stream to next level inside the level package
7038         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7039         {
7040           level->no_valid_file = TRUE;
7041
7042           Warn("cannot fseek in file '%s' -- using empty level", filename);
7043
7044           return;
7045         }
7046
7047         // skip apparently unused extra bytes following each level
7048         ReadUnusedBytesFromFile(file, extra_bytes);
7049
7050         // read size of next level in level package
7051         skip_bytes = getFile32BitLE(file);
7052
7053         num_levels_to_skip--;
7054       }
7055     }
7056     else
7057     {
7058       level->no_valid_file = TRUE;
7059
7060       Warn("unknown DC2 level file '%s' -- using empty level", filename);
7061
7062       return;
7063     }
7064   }
7065
7066   LoadLevelFromFileStream_DC(file, level);
7067
7068   closeFile(file);
7069 }
7070
7071
7072 // ----------------------------------------------------------------------------
7073 // functions for loading SB level
7074 // ----------------------------------------------------------------------------
7075
7076 int getMappedElement_SB(int element_ascii, boolean use_ces)
7077 {
7078   static struct
7079   {
7080     int ascii;
7081     int sb;
7082     int ce;
7083   }
7084   sb_element_mapping[] =
7085   {
7086     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
7087     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
7088     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
7089     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
7090     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
7091     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
7092     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
7093     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
7094
7095     { 0,   -1,                      -1          },
7096   };
7097
7098   int i;
7099
7100   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7101     if (element_ascii == sb_element_mapping[i].ascii)
7102       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7103
7104   return EL_UNDEFINED;
7105 }
7106
7107 static void SetLevelSettings_SB(struct LevelInfo *level)
7108 {
7109   // time settings
7110   level->time = 0;
7111   level->use_step_counter = TRUE;
7112
7113   // score settings
7114   level->score[SC_TIME_BONUS] = 0;
7115   level->time_score_base = 1;
7116   level->rate_time_over_score = TRUE;
7117
7118   // game settings
7119   level->auto_exit_sokoban = TRUE;
7120 }
7121
7122 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7123                                      struct LevelFileInfo *level_file_info,
7124                                      boolean level_info_only)
7125 {
7126   char *filename = level_file_info->filename;
7127   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7128   char last_comment[MAX_LINE_LEN];
7129   char level_name[MAX_LINE_LEN];
7130   char *line_ptr;
7131   File *file;
7132   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7133   boolean read_continued_line = FALSE;
7134   boolean reading_playfield = FALSE;
7135   boolean got_valid_playfield_line = FALSE;
7136   boolean invalid_playfield_char = FALSE;
7137   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7138   int file_level_nr = 0;
7139   int x = 0, y = 0;             // initialized to make compilers happy
7140
7141   last_comment[0] = '\0';
7142   level_name[0] = '\0';
7143
7144   if (!(file = openFile(filename, MODE_READ)))
7145   {
7146     level->no_valid_file = TRUE;
7147
7148     if (!level_info_only)
7149       Warn("cannot read level '%s' -- using empty level", filename);
7150
7151     return;
7152   }
7153
7154   while (!checkEndOfFile(file))
7155   {
7156     // level successfully read, but next level may follow here
7157     if (!got_valid_playfield_line && reading_playfield)
7158     {
7159       // read playfield from single level file -- skip remaining file
7160       if (!level_file_info->packed)
7161         break;
7162
7163       if (file_level_nr >= num_levels_to_skip)
7164         break;
7165
7166       file_level_nr++;
7167
7168       last_comment[0] = '\0';
7169       level_name[0] = '\0';
7170
7171       reading_playfield = FALSE;
7172     }
7173
7174     got_valid_playfield_line = FALSE;
7175
7176     // read next line of input file
7177     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7178       break;
7179
7180     // cut trailing line break (this can be newline and/or carriage return)
7181     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7182       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7183         *line_ptr = '\0';
7184
7185     // copy raw input line for later use (mainly debugging output)
7186     strcpy(line_raw, line);
7187
7188     if (read_continued_line)
7189     {
7190       // append new line to existing line, if there is enough space
7191       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7192         strcat(previous_line, line_ptr);
7193
7194       strcpy(line, previous_line);      // copy storage buffer to line
7195
7196       read_continued_line = FALSE;
7197     }
7198
7199     // if the last character is '\', continue at next line
7200     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7201     {
7202       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7203       strcpy(previous_line, line);      // copy line to storage buffer
7204
7205       read_continued_line = TRUE;
7206
7207       continue;
7208     }
7209
7210     // skip empty lines
7211     if (line[0] == '\0')
7212       continue;
7213
7214     // extract comment text from comment line
7215     if (line[0] == ';')
7216     {
7217       for (line_ptr = line; *line_ptr; line_ptr++)
7218         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7219           break;
7220
7221       strcpy(last_comment, line_ptr);
7222
7223       continue;
7224     }
7225
7226     // extract level title text from line containing level title
7227     if (line[0] == '\'')
7228     {
7229       strcpy(level_name, &line[1]);
7230
7231       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7232         level_name[strlen(level_name) - 1] = '\0';
7233
7234       continue;
7235     }
7236
7237     // skip lines containing only spaces (or empty lines)
7238     for (line_ptr = line; *line_ptr; line_ptr++)
7239       if (*line_ptr != ' ')
7240         break;
7241     if (*line_ptr == '\0')
7242       continue;
7243
7244     // at this point, we have found a line containing part of a playfield
7245
7246     got_valid_playfield_line = TRUE;
7247
7248     if (!reading_playfield)
7249     {
7250       reading_playfield = TRUE;
7251       invalid_playfield_char = FALSE;
7252
7253       for (x = 0; x < MAX_LEV_FIELDX; x++)
7254         for (y = 0; y < MAX_LEV_FIELDY; y++)
7255           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7256
7257       level->fieldx = 0;
7258       level->fieldy = 0;
7259
7260       // start with topmost tile row
7261       y = 0;
7262     }
7263
7264     // skip playfield line if larger row than allowed
7265     if (y >= MAX_LEV_FIELDY)
7266       continue;
7267
7268     // start with leftmost tile column
7269     x = 0;
7270
7271     // read playfield elements from line
7272     for (line_ptr = line; *line_ptr; line_ptr++)
7273     {
7274       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7275
7276       // stop parsing playfield line if larger column than allowed
7277       if (x >= MAX_LEV_FIELDX)
7278         break;
7279
7280       if (mapped_sb_element == EL_UNDEFINED)
7281       {
7282         invalid_playfield_char = TRUE;
7283
7284         break;
7285       }
7286
7287       level->field[x][y] = mapped_sb_element;
7288
7289       // continue with next tile column
7290       x++;
7291
7292       level->fieldx = MAX(x, level->fieldx);
7293     }
7294
7295     if (invalid_playfield_char)
7296     {
7297       // if first playfield line, treat invalid lines as comment lines
7298       if (y == 0)
7299         reading_playfield = FALSE;
7300
7301       continue;
7302     }
7303
7304     // continue with next tile row
7305     y++;
7306   }
7307
7308   closeFile(file);
7309
7310   level->fieldy = y;
7311
7312   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7313   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7314
7315   if (!reading_playfield)
7316   {
7317     level->no_valid_file = TRUE;
7318
7319     Warn("cannot read level '%s' -- using empty level", filename);
7320
7321     return;
7322   }
7323
7324   if (*level_name != '\0')
7325   {
7326     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7327     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7328   }
7329   else if (*last_comment != '\0')
7330   {
7331     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7332     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7333   }
7334   else
7335   {
7336     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7337   }
7338
7339   // set all empty fields beyond the border walls to invisible steel wall
7340   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7341   {
7342     if ((x == 0 || x == level->fieldx - 1 ||
7343          y == 0 || y == level->fieldy - 1) &&
7344         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7345       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7346                      level->field, level->fieldx, level->fieldy);
7347   }
7348
7349   // set special level settings for Sokoban levels
7350   SetLevelSettings_SB(level);
7351
7352   if (load_xsb_to_ces)
7353   {
7354     // special global settings can now be set in level template
7355     level->use_custom_template = TRUE;
7356   }
7357 }
7358
7359
7360 // -------------------------------------------------------------------------
7361 // functions for handling native levels
7362 // -------------------------------------------------------------------------
7363
7364 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7365                                      struct LevelFileInfo *level_file_info,
7366                                      boolean level_info_only)
7367 {
7368   int pos = 0;
7369
7370   // determine position of requested level inside level package
7371   if (level_file_info->packed)
7372     pos = level_file_info->nr - leveldir_current->first_level;
7373
7374   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7375     level->no_valid_file = TRUE;
7376 }
7377
7378 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7379                                      struct LevelFileInfo *level_file_info,
7380                                      boolean level_info_only)
7381 {
7382   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7383     level->no_valid_file = TRUE;
7384 }
7385
7386 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7387                                      struct LevelFileInfo *level_file_info,
7388                                      boolean level_info_only)
7389 {
7390   int pos = 0;
7391
7392   // determine position of requested level inside level package
7393   if (level_file_info->packed)
7394     pos = level_file_info->nr - leveldir_current->first_level;
7395
7396   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7397     level->no_valid_file = TRUE;
7398 }
7399
7400 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7401                                      struct LevelFileInfo *level_file_info,
7402                                      boolean level_info_only)
7403 {
7404   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7405     level->no_valid_file = TRUE;
7406 }
7407
7408 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7409 {
7410   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7411     CopyNativeLevel_RND_to_BD(level);
7412   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7413     CopyNativeLevel_RND_to_EM(level);
7414   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7415     CopyNativeLevel_RND_to_SP(level);
7416   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7417     CopyNativeLevel_RND_to_MM(level);
7418 }
7419
7420 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7421 {
7422   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7423     CopyNativeLevel_BD_to_RND(level);
7424   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7425     CopyNativeLevel_EM_to_RND(level);
7426   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7427     CopyNativeLevel_SP_to_RND(level);
7428   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7429     CopyNativeLevel_MM_to_RND(level);
7430 }
7431
7432 void SaveNativeLevel(struct LevelInfo *level)
7433 {
7434   // saving native level files only supported for some game engines
7435   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7436       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7437     return;
7438
7439   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7440                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7441   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7442   char *filename = getLevelFilenameFromBasename(basename);
7443
7444   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7445     return;
7446
7447   boolean success = FALSE;
7448
7449   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7450   {
7451     CopyNativeLevel_RND_to_BD(level);
7452     // CopyNativeTape_RND_to_BD(level);
7453
7454     success = SaveNativeLevel_BD(filename);
7455   }
7456   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7457   {
7458     CopyNativeLevel_RND_to_SP(level);
7459     CopyNativeTape_RND_to_SP(level);
7460
7461     success = SaveNativeLevel_SP(filename);
7462   }
7463
7464   if (success)
7465     Request("Native level file saved!", REQ_CONFIRM);
7466   else
7467     Request("Failed to save native level file!", REQ_CONFIRM);
7468 }
7469
7470
7471 // ----------------------------------------------------------------------------
7472 // functions for loading generic level
7473 // ----------------------------------------------------------------------------
7474
7475 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7476                                   struct LevelFileInfo *level_file_info,
7477                                   boolean level_info_only)
7478 {
7479   // always start with reliable default values
7480   setLevelInfoToDefaults(level, level_info_only, TRUE);
7481
7482   switch (level_file_info->type)
7483   {
7484     case LEVEL_FILE_TYPE_RND:
7485       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7486       break;
7487
7488     case LEVEL_FILE_TYPE_BD:
7489       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7490       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7491       break;
7492
7493     case LEVEL_FILE_TYPE_EM:
7494       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7495       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7496       break;
7497
7498     case LEVEL_FILE_TYPE_SP:
7499       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7500       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7501       break;
7502
7503     case LEVEL_FILE_TYPE_MM:
7504       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7505       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7506       break;
7507
7508     case LEVEL_FILE_TYPE_DC:
7509       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7510       break;
7511
7512     case LEVEL_FILE_TYPE_SB:
7513       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7514       break;
7515
7516     default:
7517       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7518       break;
7519   }
7520
7521   // if level file is invalid, restore level structure to default values
7522   if (level->no_valid_file)
7523     setLevelInfoToDefaults(level, level_info_only, FALSE);
7524
7525   if (check_special_flags("use_native_bd_game_engine"))
7526     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7527
7528   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7529     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7530
7531   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7532     CopyNativeLevel_Native_to_RND(level);
7533 }
7534
7535 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7536 {
7537   static struct LevelFileInfo level_file_info;
7538
7539   // always start with reliable default values
7540   setFileInfoToDefaults(&level_file_info);
7541
7542   level_file_info.nr = 0;                       // unknown level number
7543   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7544
7545   setString(&level_file_info.filename, filename);
7546
7547   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7548 }
7549
7550 static void LoadLevel_InitVersion(struct LevelInfo *level)
7551 {
7552   int i, j;
7553
7554   if (leveldir_current == NULL)         // only when dumping level
7555     return;
7556
7557   // all engine modifications also valid for levels which use latest engine
7558   if (level->game_version < VERSION_IDENT(3,2,0,5))
7559   {
7560     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7561     level->time_score_base = 10;
7562   }
7563
7564   if (leveldir_current->latest_engine)
7565   {
7566     // ---------- use latest game engine --------------------------------------
7567
7568     /* For all levels which are forced to use the latest game engine version
7569        (normally all but user contributed, private and undefined levels), set
7570        the game engine version to the actual version; this allows for actual
7571        corrections in the game engine to take effect for existing, converted
7572        levels (from "classic" or other existing games) to make the emulation
7573        of the corresponding game more accurate, while (hopefully) not breaking
7574        existing levels created from other players. */
7575
7576     level->game_version = GAME_VERSION_ACTUAL;
7577
7578     /* Set special EM style gems behaviour: EM style gems slip down from
7579        normal, steel and growing wall. As this is a more fundamental change,
7580        it seems better to set the default behaviour to "off" (as it is more
7581        natural) and make it configurable in the level editor (as a property
7582        of gem style elements). Already existing converted levels (neither
7583        private nor contributed levels) are changed to the new behaviour. */
7584
7585     if (level->file_version < FILE_VERSION_2_0)
7586       level->em_slippery_gems = TRUE;
7587
7588     return;
7589   }
7590
7591   // ---------- use game engine the level was created with --------------------
7592
7593   /* For all levels which are not forced to use the latest game engine
7594      version (normally user contributed, private and undefined levels),
7595      use the version of the game engine the levels were created for.
7596
7597      Since 2.0.1, the game engine version is now directly stored
7598      in the level file (chunk "VERS"), so there is no need anymore
7599      to set the game version from the file version (except for old,
7600      pre-2.0 levels, where the game version is still taken from the
7601      file format version used to store the level -- see above). */
7602
7603   // player was faster than enemies in 1.0.0 and before
7604   if (level->file_version == FILE_VERSION_1_0)
7605     for (i = 0; i < MAX_PLAYERS; i++)
7606       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7607
7608   // default behaviour for EM style gems was "slippery" only in 2.0.1
7609   if (level->game_version == VERSION_IDENT(2,0,1,0))
7610     level->em_slippery_gems = TRUE;
7611
7612   // springs could be pushed over pits before (pre-release version) 2.2.0
7613   if (level->game_version < VERSION_IDENT(2,2,0,0))
7614     level->use_spring_bug = TRUE;
7615
7616   if (level->game_version < VERSION_IDENT(3,2,0,5))
7617   {
7618     // time orb caused limited time in endless time levels before 3.2.0-5
7619     level->use_time_orb_bug = TRUE;
7620
7621     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7622     level->block_snap_field = FALSE;
7623
7624     // extra time score was same value as time left score before 3.2.0-5
7625     level->extra_time_score = level->score[SC_TIME_BONUS];
7626   }
7627
7628   if (level->game_version < VERSION_IDENT(3,2,0,7))
7629   {
7630     // default behaviour for snapping was "not continuous" before 3.2.0-7
7631     level->continuous_snapping = FALSE;
7632   }
7633
7634   // only few elements were able to actively move into acid before 3.1.0
7635   // trigger settings did not exist before 3.1.0; set to default "any"
7636   if (level->game_version < VERSION_IDENT(3,1,0,0))
7637   {
7638     // correct "can move into acid" settings (all zero in old levels)
7639
7640     level->can_move_into_acid_bits = 0; // nothing can move into acid
7641     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7642
7643     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7644     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7645     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7646     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7647
7648     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7649       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7650
7651     // correct trigger settings (stored as zero == "none" in old levels)
7652
7653     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7654     {
7655       int element = EL_CUSTOM_START + i;
7656       struct ElementInfo *ei = &element_info[element];
7657
7658       for (j = 0; j < ei->num_change_pages; j++)
7659       {
7660         struct ElementChangeInfo *change = &ei->change_page[j];
7661
7662         change->trigger_player = CH_PLAYER_ANY;
7663         change->trigger_page = CH_PAGE_ANY;
7664       }
7665     }
7666   }
7667
7668   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7669   {
7670     int element = EL_CUSTOM_256;
7671     struct ElementInfo *ei = &element_info[element];
7672     struct ElementChangeInfo *change = &ei->change_page[0];
7673
7674     /* This is needed to fix a problem that was caused by a bugfix in function
7675        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7676        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7677        not replace walkable elements, but instead just placed the player on it,
7678        without placing the Sokoban field under the player). Unfortunately, this
7679        breaks "Snake Bite" style levels when the snake is halfway through a door
7680        that just closes (the snake head is still alive and can be moved in this
7681        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7682        player (without Sokoban element) which then gets killed as designed). */
7683
7684     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7685          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7686         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7687       change->target_element = EL_PLAYER_1;
7688   }
7689
7690   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7691   if (level->game_version < VERSION_IDENT(3,2,5,0))
7692   {
7693     /* This is needed to fix a problem that was caused by a bugfix in function
7694        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7695        corrects the behaviour when a custom element changes to another custom
7696        element with a higher element number that has change actions defined.
7697        Normally, only one change per frame is allowed for custom elements.
7698        Therefore, it is checked if a custom element already changed in the
7699        current frame; if it did, subsequent changes are suppressed.
7700        Unfortunately, this is only checked for element changes, but not for
7701        change actions, which are still executed. As the function above loops
7702        through all custom elements from lower to higher, an element change
7703        resulting in a lower CE number won't be checked again, while a target
7704        element with a higher number will also be checked, and potential change
7705        actions will get executed for this CE, too (which is wrong), while
7706        further changes are ignored (which is correct). As this bugfix breaks
7707        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7708        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7709        behaviour for existing levels and tapes that make use of this bug */
7710
7711     level->use_action_after_change_bug = TRUE;
7712   }
7713
7714   // not centering level after relocating player was default only in 3.2.3
7715   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7716     level->shifted_relocation = TRUE;
7717
7718   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7719   if (level->game_version < VERSION_IDENT(3,2,6,0))
7720     level->em_explodes_by_fire = TRUE;
7721
7722   // levels were solved by the first player entering an exit up to 4.1.0.0
7723   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7724     level->solved_by_one_player = TRUE;
7725
7726   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7727   if (level->game_version < VERSION_IDENT(4,1,1,1))
7728     level->use_life_bugs = TRUE;
7729
7730   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7731   if (level->game_version < VERSION_IDENT(4,1,1,1))
7732     level->sb_objects_needed = FALSE;
7733
7734   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7735   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7736     level->finish_dig_collect = FALSE;
7737
7738   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7739   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7740     level->keep_walkable_ce = TRUE;
7741 }
7742
7743 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7744 {
7745   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7746   int x, y;
7747
7748   // check if this level is (not) a Sokoban level
7749   for (y = 0; y < level->fieldy; y++)
7750     for (x = 0; x < level->fieldx; x++)
7751       if (!IS_SB_ELEMENT(Tile[x][y]))
7752         is_sokoban_level = FALSE;
7753
7754   if (is_sokoban_level)
7755   {
7756     // set special level settings for Sokoban levels
7757     SetLevelSettings_SB(level);
7758   }
7759 }
7760
7761 static void LoadLevel_InitSettings(struct LevelInfo *level)
7762 {
7763   // adjust level settings for (non-native) Sokoban-style levels
7764   LoadLevel_InitSettings_SB(level);
7765
7766   // rename levels with title "nameless level" or if renaming is forced
7767   if (leveldir_current->empty_level_name != NULL &&
7768       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7769        leveldir_current->force_level_name))
7770     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7771              leveldir_current->empty_level_name, level_nr);
7772 }
7773
7774 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7775 {
7776   int i, x, y;
7777
7778   // map elements that have changed in newer versions
7779   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7780                                                     level->game_version);
7781   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7782     for (x = 0; x < 3; x++)
7783       for (y = 0; y < 3; y++)
7784         level->yamyam_content[i].e[x][y] =
7785           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7786                                     level->game_version);
7787
7788 }
7789
7790 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7791 {
7792   int i, j;
7793
7794   // map custom element change events that have changed in newer versions
7795   // (these following values were accidentally changed in version 3.0.1)
7796   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7797   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7798   {
7799     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7800     {
7801       int element = EL_CUSTOM_START + i;
7802
7803       // order of checking and copying events to be mapped is important
7804       // (do not change the start and end value -- they are constant)
7805       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7806       {
7807         if (HAS_CHANGE_EVENT(element, j - 2))
7808         {
7809           SET_CHANGE_EVENT(element, j - 2, FALSE);
7810           SET_CHANGE_EVENT(element, j, TRUE);
7811         }
7812       }
7813
7814       // order of checking and copying events to be mapped is important
7815       // (do not change the start and end value -- they are constant)
7816       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7817       {
7818         if (HAS_CHANGE_EVENT(element, j - 1))
7819         {
7820           SET_CHANGE_EVENT(element, j - 1, FALSE);
7821           SET_CHANGE_EVENT(element, j, TRUE);
7822         }
7823       }
7824     }
7825   }
7826
7827   // initialize "can_change" field for old levels with only one change page
7828   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7829   {
7830     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7831     {
7832       int element = EL_CUSTOM_START + i;
7833
7834       if (CAN_CHANGE(element))
7835         element_info[element].change->can_change = TRUE;
7836     }
7837   }
7838
7839   // correct custom element values (for old levels without these options)
7840   if (level->game_version < VERSION_IDENT(3,1,1,0))
7841   {
7842     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7843     {
7844       int element = EL_CUSTOM_START + i;
7845       struct ElementInfo *ei = &element_info[element];
7846
7847       if (ei->access_direction == MV_NO_DIRECTION)
7848         ei->access_direction = MV_ALL_DIRECTIONS;
7849     }
7850   }
7851
7852   // correct custom element values (fix invalid values for all versions)
7853   if (1)
7854   {
7855     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7856     {
7857       int element = EL_CUSTOM_START + i;
7858       struct ElementInfo *ei = &element_info[element];
7859
7860       for (j = 0; j < ei->num_change_pages; j++)
7861       {
7862         struct ElementChangeInfo *change = &ei->change_page[j];
7863
7864         if (change->trigger_player == CH_PLAYER_NONE)
7865           change->trigger_player = CH_PLAYER_ANY;
7866
7867         if (change->trigger_side == CH_SIDE_NONE)
7868           change->trigger_side = CH_SIDE_ANY;
7869       }
7870     }
7871   }
7872
7873   // initialize "can_explode" field for old levels which did not store this
7874   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7875   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7876   {
7877     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7878     {
7879       int element = EL_CUSTOM_START + i;
7880
7881       if (EXPLODES_1X1_OLD(element))
7882         element_info[element].explosion_type = EXPLODES_1X1;
7883
7884       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7885                                              EXPLODES_SMASHED(element) ||
7886                                              EXPLODES_IMPACT(element)));
7887     }
7888   }
7889
7890   // correct previously hard-coded move delay values for maze runner style
7891   if (level->game_version < VERSION_IDENT(3,1,1,0))
7892   {
7893     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7894     {
7895       int element = EL_CUSTOM_START + i;
7896
7897       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7898       {
7899         // previously hard-coded and therefore ignored
7900         element_info[element].move_delay_fixed = 9;
7901         element_info[element].move_delay_random = 0;
7902       }
7903     }
7904   }
7905
7906   // set some other uninitialized values of custom elements in older levels
7907   if (level->game_version < VERSION_IDENT(3,1,0,0))
7908   {
7909     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7910     {
7911       int element = EL_CUSTOM_START + i;
7912
7913       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7914
7915       element_info[element].explosion_delay = 17;
7916       element_info[element].ignition_delay = 8;
7917     }
7918   }
7919
7920   // set mouse click change events to work for left/middle/right mouse button
7921   if (level->game_version < VERSION_IDENT(4,2,3,0))
7922   {
7923     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7924     {
7925       int element = EL_CUSTOM_START + i;
7926       struct ElementInfo *ei = &element_info[element];
7927
7928       for (j = 0; j < ei->num_change_pages; j++)
7929       {
7930         struct ElementChangeInfo *change = &ei->change_page[j];
7931
7932         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7933             change->has_event[CE_PRESSED_BY_MOUSE] ||
7934             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7935             change->has_event[CE_MOUSE_PRESSED_ON_X])
7936           change->trigger_side = CH_SIDE_ANY;
7937       }
7938     }
7939   }
7940 }
7941
7942 static void LoadLevel_InitElements(struct LevelInfo *level)
7943 {
7944   LoadLevel_InitStandardElements(level);
7945
7946   if (level->file_has_custom_elements)
7947     LoadLevel_InitCustomElements(level);
7948
7949   // initialize element properties for level editor etc.
7950   InitElementPropertiesEngine(level->game_version);
7951   InitElementPropertiesGfxElement();
7952 }
7953
7954 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7955 {
7956   int x, y;
7957
7958   // map elements that have changed in newer versions
7959   for (y = 0; y < level->fieldy; y++)
7960     for (x = 0; x < level->fieldx; x++)
7961       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7962                                                      level->game_version);
7963
7964   // clear unused playfield data (nicer if level gets resized in editor)
7965   for (x = 0; x < MAX_LEV_FIELDX; x++)
7966     for (y = 0; y < MAX_LEV_FIELDY; y++)
7967       if (x >= level->fieldx || y >= level->fieldy)
7968         level->field[x][y] = EL_EMPTY;
7969
7970   // copy elements to runtime playfield array
7971   for (x = 0; x < MAX_LEV_FIELDX; x++)
7972     for (y = 0; y < MAX_LEV_FIELDY; y++)
7973       Tile[x][y] = level->field[x][y];
7974
7975   // initialize level size variables for faster access
7976   lev_fieldx = level->fieldx;
7977   lev_fieldy = level->fieldy;
7978
7979   // determine border element for this level
7980   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7981     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7982   else
7983     SetBorderElement();
7984 }
7985
7986 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7987 {
7988   struct LevelFileInfo *level_file_info = &level->file_info;
7989
7990   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7991     CopyNativeLevel_RND_to_Native(level);
7992 }
7993
7994 static void LoadLevelTemplate_LoadAndInit(void)
7995 {
7996   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7997
7998   LoadLevel_InitVersion(&level_template);
7999   LoadLevel_InitElements(&level_template);
8000   LoadLevel_InitSettings(&level_template);
8001
8002   ActivateLevelTemplate();
8003 }
8004
8005 void LoadLevelTemplate(int nr)
8006 {
8007   if (!fileExists(getGlobalLevelTemplateFilename()))
8008   {
8009     Warn("no level template found for this level");
8010
8011     return;
8012   }
8013
8014   setLevelFileInfo(&level_template.file_info, nr);
8015
8016   LoadLevelTemplate_LoadAndInit();
8017 }
8018
8019 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8020 {
8021   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8022
8023   LoadLevelTemplate_LoadAndInit();
8024 }
8025
8026 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8027 {
8028   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8029
8030   if (level.use_custom_template)
8031   {
8032     if (network_level != NULL)
8033       LoadNetworkLevelTemplate(network_level);
8034     else
8035       LoadLevelTemplate(-1);
8036   }
8037
8038   LoadLevel_InitVersion(&level);
8039   LoadLevel_InitElements(&level);
8040   LoadLevel_InitPlayfield(&level);
8041   LoadLevel_InitSettings(&level);
8042
8043   LoadLevel_InitNativeEngines(&level);
8044 }
8045
8046 void LoadLevel(int nr)
8047 {
8048   SetLevelSetInfo(leveldir_current->identifier, nr);
8049
8050   setLevelFileInfo(&level.file_info, nr);
8051
8052   LoadLevel_LoadAndInit(NULL);
8053 }
8054
8055 void LoadLevelInfoOnly(int nr)
8056 {
8057   setLevelFileInfo(&level.file_info, nr);
8058
8059   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8060 }
8061
8062 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8063 {
8064   SetLevelSetInfo(network_level->leveldir_identifier,
8065                   network_level->file_info.nr);
8066
8067   copyLevelFileInfo(&network_level->file_info, &level.file_info);
8068
8069   LoadLevel_LoadAndInit(network_level);
8070 }
8071
8072 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8073 {
8074   int chunk_size = 0;
8075
8076   chunk_size += putFileVersion(file, level->file_version);
8077   chunk_size += putFileVersion(file, level->game_version);
8078
8079   return chunk_size;
8080 }
8081
8082 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8083 {
8084   int chunk_size = 0;
8085
8086   chunk_size += putFile16BitBE(file, level->creation_date.year);
8087   chunk_size += putFile8Bit(file,    level->creation_date.month);
8088   chunk_size += putFile8Bit(file,    level->creation_date.day);
8089
8090   return chunk_size;
8091 }
8092
8093 #if ENABLE_HISTORIC_CHUNKS
8094 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8095 {
8096   int i, x, y;
8097
8098   putFile8Bit(file, level->fieldx);
8099   putFile8Bit(file, level->fieldy);
8100
8101   putFile16BitBE(file, level->time);
8102   putFile16BitBE(file, level->gems_needed);
8103
8104   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8105     putFile8Bit(file, level->name[i]);
8106
8107   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8108     putFile8Bit(file, level->score[i]);
8109
8110   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8111     for (y = 0; y < 3; y++)
8112       for (x = 0; x < 3; x++)
8113         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8114                            level->yamyam_content[i].e[x][y]));
8115   putFile8Bit(file, level->amoeba_speed);
8116   putFile8Bit(file, level->time_magic_wall);
8117   putFile8Bit(file, level->time_wheel);
8118   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8119                      level->amoeba_content));
8120   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8121   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8122   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8123   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8124
8125   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8126
8127   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8128   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8129   putFile32BitBE(file, level->can_move_into_acid_bits);
8130   putFile8Bit(file, level->dont_collide_with_bits);
8131
8132   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8133   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8134
8135   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8136   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8137   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8138
8139   putFile8Bit(file, level->game_engine_type);
8140
8141   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8142 }
8143 #endif
8144
8145 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8146 {
8147   int chunk_size = 0;
8148   int i;
8149
8150   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8151     chunk_size += putFile8Bit(file, level->name[i]);
8152
8153   return chunk_size;
8154 }
8155
8156 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8157 {
8158   int chunk_size = 0;
8159   int i;
8160
8161   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8162     chunk_size += putFile8Bit(file, level->author[i]);
8163
8164   return chunk_size;
8165 }
8166
8167 #if ENABLE_HISTORIC_CHUNKS
8168 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8169 {
8170   int chunk_size = 0;
8171   int x, y;
8172
8173   for (y = 0; y < level->fieldy; y++)
8174     for (x = 0; x < level->fieldx; x++)
8175       if (level->encoding_16bit_field)
8176         chunk_size += putFile16BitBE(file, level->field[x][y]);
8177       else
8178         chunk_size += putFile8Bit(file, level->field[x][y]);
8179
8180   return chunk_size;
8181 }
8182 #endif
8183
8184 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8185 {
8186   int chunk_size = 0;
8187   int x, y;
8188
8189   for (y = 0; y < level->fieldy; y++) 
8190     for (x = 0; x < level->fieldx; x++) 
8191       chunk_size += putFile16BitBE(file, level->field[x][y]);
8192
8193   return chunk_size;
8194 }
8195
8196 #if ENABLE_HISTORIC_CHUNKS
8197 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8198 {
8199   int i, x, y;
8200
8201   putFile8Bit(file, EL_YAMYAM);
8202   putFile8Bit(file, level->num_yamyam_contents);
8203   putFile8Bit(file, 0);
8204   putFile8Bit(file, 0);
8205
8206   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8207     for (y = 0; y < 3; y++)
8208       for (x = 0; x < 3; x++)
8209         if (level->encoding_16bit_field)
8210           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8211         else
8212           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8213 }
8214 #endif
8215
8216 #if ENABLE_HISTORIC_CHUNKS
8217 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8218 {
8219   int i, x, y;
8220   int num_contents, content_xsize, content_ysize;
8221   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8222
8223   if (element == EL_YAMYAM)
8224   {
8225     num_contents = level->num_yamyam_contents;
8226     content_xsize = 3;
8227     content_ysize = 3;
8228
8229     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8230       for (y = 0; y < 3; y++)
8231         for (x = 0; x < 3; x++)
8232           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8233   }
8234   else if (element == EL_BD_AMOEBA)
8235   {
8236     num_contents = 1;
8237     content_xsize = 1;
8238     content_ysize = 1;
8239
8240     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8241       for (y = 0; y < 3; y++)
8242         for (x = 0; x < 3; x++)
8243           content_array[i][x][y] = EL_EMPTY;
8244     content_array[0][0][0] = level->amoeba_content;
8245   }
8246   else
8247   {
8248     // chunk header already written -- write empty chunk data
8249     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8250
8251     Warn("cannot save content for element '%d'", element);
8252
8253     return;
8254   }
8255
8256   putFile16BitBE(file, element);
8257   putFile8Bit(file, num_contents);
8258   putFile8Bit(file, content_xsize);
8259   putFile8Bit(file, content_ysize);
8260
8261   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8262
8263   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8264     for (y = 0; y < 3; y++)
8265       for (x = 0; x < 3; x++)
8266         putFile16BitBE(file, content_array[i][x][y]);
8267 }
8268 #endif
8269
8270 #if ENABLE_HISTORIC_CHUNKS
8271 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8272 {
8273   int envelope_nr = element - EL_ENVELOPE_1;
8274   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8275   int chunk_size = 0;
8276   int i;
8277
8278   chunk_size += putFile16BitBE(file, element);
8279   chunk_size += putFile16BitBE(file, envelope_len);
8280   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8281   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8282
8283   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8284   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8285
8286   for (i = 0; i < envelope_len; i++)
8287     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8288
8289   return chunk_size;
8290 }
8291 #endif
8292
8293 #if ENABLE_HISTORIC_CHUNKS
8294 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8295                            int num_changed_custom_elements)
8296 {
8297   int i, check = 0;
8298
8299   putFile16BitBE(file, num_changed_custom_elements);
8300
8301   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8302   {
8303     int element = EL_CUSTOM_START + i;
8304
8305     struct ElementInfo *ei = &element_info[element];
8306
8307     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8308     {
8309       if (check < num_changed_custom_elements)
8310       {
8311         putFile16BitBE(file, element);
8312         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8313       }
8314
8315       check++;
8316     }
8317   }
8318
8319   if (check != num_changed_custom_elements)     // should not happen
8320     Warn("inconsistent number of custom element properties");
8321 }
8322 #endif
8323
8324 #if ENABLE_HISTORIC_CHUNKS
8325 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8326                            int num_changed_custom_elements)
8327 {
8328   int i, check = 0;
8329
8330   putFile16BitBE(file, num_changed_custom_elements);
8331
8332   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8333   {
8334     int element = EL_CUSTOM_START + i;
8335
8336     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8337     {
8338       if (check < num_changed_custom_elements)
8339       {
8340         putFile16BitBE(file, element);
8341         putFile16BitBE(file, element_info[element].change->target_element);
8342       }
8343
8344       check++;
8345     }
8346   }
8347
8348   if (check != num_changed_custom_elements)     // should not happen
8349     Warn("inconsistent number of custom target elements");
8350 }
8351 #endif
8352
8353 #if ENABLE_HISTORIC_CHUNKS
8354 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8355                            int num_changed_custom_elements)
8356 {
8357   int i, j, x, y, check = 0;
8358
8359   putFile16BitBE(file, num_changed_custom_elements);
8360
8361   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8362   {
8363     int element = EL_CUSTOM_START + i;
8364     struct ElementInfo *ei = &element_info[element];
8365
8366     if (ei->modified_settings)
8367     {
8368       if (check < num_changed_custom_elements)
8369       {
8370         putFile16BitBE(file, element);
8371
8372         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8373           putFile8Bit(file, ei->description[j]);
8374
8375         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8376
8377         // some free bytes for future properties and padding
8378         WriteUnusedBytesToFile(file, 7);
8379
8380         putFile8Bit(file, ei->use_gfx_element);
8381         putFile16BitBE(file, ei->gfx_element_initial);
8382
8383         putFile8Bit(file, ei->collect_score_initial);
8384         putFile8Bit(file, ei->collect_count_initial);
8385
8386         putFile16BitBE(file, ei->push_delay_fixed);
8387         putFile16BitBE(file, ei->push_delay_random);
8388         putFile16BitBE(file, ei->move_delay_fixed);
8389         putFile16BitBE(file, ei->move_delay_random);
8390
8391         putFile16BitBE(file, ei->move_pattern);
8392         putFile8Bit(file, ei->move_direction_initial);
8393         putFile8Bit(file, ei->move_stepsize);
8394
8395         for (y = 0; y < 3; y++)
8396           for (x = 0; x < 3; x++)
8397             putFile16BitBE(file, ei->content.e[x][y]);
8398
8399         putFile32BitBE(file, ei->change->events);
8400
8401         putFile16BitBE(file, ei->change->target_element);
8402
8403         putFile16BitBE(file, ei->change->delay_fixed);
8404         putFile16BitBE(file, ei->change->delay_random);
8405         putFile16BitBE(file, ei->change->delay_frames);
8406
8407         putFile16BitBE(file, ei->change->initial_trigger_element);
8408
8409         putFile8Bit(file, ei->change->explode);
8410         putFile8Bit(file, ei->change->use_target_content);
8411         putFile8Bit(file, ei->change->only_if_complete);
8412         putFile8Bit(file, ei->change->use_random_replace);
8413
8414         putFile8Bit(file, ei->change->random_percentage);
8415         putFile8Bit(file, ei->change->replace_when);
8416
8417         for (y = 0; y < 3; y++)
8418           for (x = 0; x < 3; x++)
8419             putFile16BitBE(file, ei->change->content.e[x][y]);
8420
8421         putFile8Bit(file, ei->slippery_type);
8422
8423         // some free bytes for future properties and padding
8424         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8425       }
8426
8427       check++;
8428     }
8429   }
8430
8431   if (check != num_changed_custom_elements)     // should not happen
8432     Warn("inconsistent number of custom element properties");
8433 }
8434 #endif
8435
8436 #if ENABLE_HISTORIC_CHUNKS
8437 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8438 {
8439   struct ElementInfo *ei = &element_info[element];
8440   int i, j, x, y;
8441
8442   // ---------- custom element base property values (96 bytes) ----------------
8443
8444   putFile16BitBE(file, element);
8445
8446   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8447     putFile8Bit(file, ei->description[i]);
8448
8449   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8450
8451   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8452
8453   putFile8Bit(file, ei->num_change_pages);
8454
8455   putFile16BitBE(file, ei->ce_value_fixed_initial);
8456   putFile16BitBE(file, ei->ce_value_random_initial);
8457   putFile8Bit(file, ei->use_last_ce_value);
8458
8459   putFile8Bit(file, ei->use_gfx_element);
8460   putFile16BitBE(file, ei->gfx_element_initial);
8461
8462   putFile8Bit(file, ei->collect_score_initial);
8463   putFile8Bit(file, ei->collect_count_initial);
8464
8465   putFile8Bit(file, ei->drop_delay_fixed);
8466   putFile8Bit(file, ei->push_delay_fixed);
8467   putFile8Bit(file, ei->drop_delay_random);
8468   putFile8Bit(file, ei->push_delay_random);
8469   putFile16BitBE(file, ei->move_delay_fixed);
8470   putFile16BitBE(file, ei->move_delay_random);
8471
8472   // bits 0 - 15 of "move_pattern" ...
8473   putFile16BitBE(file, ei->move_pattern & 0xffff);
8474   putFile8Bit(file, ei->move_direction_initial);
8475   putFile8Bit(file, ei->move_stepsize);
8476
8477   putFile8Bit(file, ei->slippery_type);
8478
8479   for (y = 0; y < 3; y++)
8480     for (x = 0; x < 3; x++)
8481       putFile16BitBE(file, ei->content.e[x][y]);
8482
8483   putFile16BitBE(file, ei->move_enter_element);
8484   putFile16BitBE(file, ei->move_leave_element);
8485   putFile8Bit(file, ei->move_leave_type);
8486
8487   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8488   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8489
8490   putFile8Bit(file, ei->access_direction);
8491
8492   putFile8Bit(file, ei->explosion_delay);
8493   putFile8Bit(file, ei->ignition_delay);
8494   putFile8Bit(file, ei->explosion_type);
8495
8496   // some free bytes for future custom property values and padding
8497   WriteUnusedBytesToFile(file, 1);
8498
8499   // ---------- change page property values (48 bytes) ------------------------
8500
8501   for (i = 0; i < ei->num_change_pages; i++)
8502   {
8503     struct ElementChangeInfo *change = &ei->change_page[i];
8504     unsigned int event_bits;
8505
8506     // bits 0 - 31 of "has_event[]" ...
8507     event_bits = 0;
8508     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8509       if (change->has_event[j])
8510         event_bits |= (1u << j);
8511     putFile32BitBE(file, event_bits);
8512
8513     putFile16BitBE(file, change->target_element);
8514
8515     putFile16BitBE(file, change->delay_fixed);
8516     putFile16BitBE(file, change->delay_random);
8517     putFile16BitBE(file, change->delay_frames);
8518
8519     putFile16BitBE(file, change->initial_trigger_element);
8520
8521     putFile8Bit(file, change->explode);
8522     putFile8Bit(file, change->use_target_content);
8523     putFile8Bit(file, change->only_if_complete);
8524     putFile8Bit(file, change->use_random_replace);
8525
8526     putFile8Bit(file, change->random_percentage);
8527     putFile8Bit(file, change->replace_when);
8528
8529     for (y = 0; y < 3; y++)
8530       for (x = 0; x < 3; x++)
8531         putFile16BitBE(file, change->target_content.e[x][y]);
8532
8533     putFile8Bit(file, change->can_change);
8534
8535     putFile8Bit(file, change->trigger_side);
8536
8537     putFile8Bit(file, change->trigger_player);
8538     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8539                        log_2(change->trigger_page)));
8540
8541     putFile8Bit(file, change->has_action);
8542     putFile8Bit(file, change->action_type);
8543     putFile8Bit(file, change->action_mode);
8544     putFile16BitBE(file, change->action_arg);
8545
8546     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8547     event_bits = 0;
8548     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8549       if (change->has_event[j])
8550         event_bits |= (1u << (j - 32));
8551     putFile8Bit(file, event_bits);
8552   }
8553 }
8554 #endif
8555
8556 #if ENABLE_HISTORIC_CHUNKS
8557 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8558 {
8559   struct ElementInfo *ei = &element_info[element];
8560   struct ElementGroupInfo *group = ei->group;
8561   int i;
8562
8563   putFile16BitBE(file, element);
8564
8565   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8566     putFile8Bit(file, ei->description[i]);
8567
8568   putFile8Bit(file, group->num_elements);
8569
8570   putFile8Bit(file, ei->use_gfx_element);
8571   putFile16BitBE(file, ei->gfx_element_initial);
8572
8573   putFile8Bit(file, group->choice_mode);
8574
8575   // some free bytes for future values and padding
8576   WriteUnusedBytesToFile(file, 3);
8577
8578   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8579     putFile16BitBE(file, group->element[i]);
8580 }
8581 #endif
8582
8583 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8584                                 boolean write_element)
8585 {
8586   int save_type = entry->save_type;
8587   int data_type = entry->data_type;
8588   int conf_type = entry->conf_type;
8589   int byte_mask = conf_type & CONF_MASK_BYTES;
8590   int element = entry->element;
8591   int default_value = entry->default_value;
8592   int num_bytes = 0;
8593   boolean modified = FALSE;
8594
8595   if (byte_mask != CONF_MASK_MULTI_BYTES)
8596   {
8597     void *value_ptr = entry->value;
8598     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8599                  *(int *)value_ptr);
8600
8601     // check if any settings have been modified before saving them
8602     if (value != default_value)
8603       modified = TRUE;
8604
8605     // do not save if explicitly told or if unmodified default settings
8606     if ((save_type == SAVE_CONF_NEVER) ||
8607         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8608       return 0;
8609
8610     if (write_element)
8611       num_bytes += putFile16BitBE(file, element);
8612
8613     num_bytes += putFile8Bit(file, conf_type);
8614     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8615                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8616                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8617                   0);
8618   }
8619   else if (data_type == TYPE_STRING)
8620   {
8621     char *default_string = entry->default_string;
8622     char *string = (char *)(entry->value);
8623     int string_length = strlen(string);
8624     int i;
8625
8626     // check if any settings have been modified before saving them
8627     if (!strEqual(string, default_string))
8628       modified = TRUE;
8629
8630     // do not save if explicitly told or if unmodified default settings
8631     if ((save_type == SAVE_CONF_NEVER) ||
8632         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8633       return 0;
8634
8635     if (write_element)
8636       num_bytes += putFile16BitBE(file, element);
8637
8638     num_bytes += putFile8Bit(file, conf_type);
8639     num_bytes += putFile16BitBE(file, string_length);
8640
8641     for (i = 0; i < string_length; i++)
8642       num_bytes += putFile8Bit(file, string[i]);
8643   }
8644   else if (data_type == TYPE_ELEMENT_LIST)
8645   {
8646     int *element_array = (int *)(entry->value);
8647     int num_elements = *(int *)(entry->num_entities);
8648     int i;
8649
8650     // check if any settings have been modified before saving them
8651     for (i = 0; i < num_elements; i++)
8652       if (element_array[i] != default_value)
8653         modified = TRUE;
8654
8655     // do not save if explicitly told or if unmodified default settings
8656     if ((save_type == SAVE_CONF_NEVER) ||
8657         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8658       return 0;
8659
8660     if (write_element)
8661       num_bytes += putFile16BitBE(file, element);
8662
8663     num_bytes += putFile8Bit(file, conf_type);
8664     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8665
8666     for (i = 0; i < num_elements; i++)
8667       num_bytes += putFile16BitBE(file, element_array[i]);
8668   }
8669   else if (data_type == TYPE_CONTENT_LIST)
8670   {
8671     struct Content *content = (struct Content *)(entry->value);
8672     int num_contents = *(int *)(entry->num_entities);
8673     int i, x, y;
8674
8675     // check if any settings have been modified before saving them
8676     for (i = 0; i < num_contents; i++)
8677       for (y = 0; y < 3; y++)
8678         for (x = 0; x < 3; x++)
8679           if (content[i].e[x][y] != default_value)
8680             modified = TRUE;
8681
8682     // do not save if explicitly told or if unmodified default settings
8683     if ((save_type == SAVE_CONF_NEVER) ||
8684         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8685       return 0;
8686
8687     if (write_element)
8688       num_bytes += putFile16BitBE(file, element);
8689
8690     num_bytes += putFile8Bit(file, conf_type);
8691     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8692
8693     for (i = 0; i < num_contents; i++)
8694       for (y = 0; y < 3; y++)
8695         for (x = 0; x < 3; x++)
8696           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8697   }
8698
8699   return num_bytes;
8700 }
8701
8702 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8703 {
8704   int chunk_size = 0;
8705   int i;
8706
8707   li = *level;          // copy level data into temporary buffer
8708
8709   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8710     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8711
8712   return chunk_size;
8713 }
8714
8715 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8716 {
8717   int chunk_size = 0;
8718   int i;
8719
8720   li = *level;          // copy level data into temporary buffer
8721
8722   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8723     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8724
8725   return chunk_size;
8726 }
8727
8728 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8729 {
8730   int envelope_nr = element - EL_ENVELOPE_1;
8731   int chunk_size = 0;
8732   int i;
8733
8734   chunk_size += putFile16BitBE(file, element);
8735
8736   // copy envelope data into temporary buffer
8737   xx_envelope = level->envelope[envelope_nr];
8738
8739   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8740     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8741
8742   return chunk_size;
8743 }
8744
8745 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8746 {
8747   struct ElementInfo *ei = &element_info[element];
8748   int chunk_size = 0;
8749   int i, j;
8750
8751   chunk_size += putFile16BitBE(file, element);
8752
8753   xx_ei = *ei;          // copy element data into temporary buffer
8754
8755   // set default description string for this specific element
8756   strcpy(xx_default_description, getDefaultElementDescription(ei));
8757
8758   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8759     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8760
8761   for (i = 0; i < ei->num_change_pages; i++)
8762   {
8763     struct ElementChangeInfo *change = &ei->change_page[i];
8764
8765     xx_current_change_page = i;
8766
8767     xx_change = *change;        // copy change data into temporary buffer
8768
8769     resetEventBits();
8770     setEventBitsFromEventFlags(change);
8771
8772     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8773       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8774                                          FALSE);
8775   }
8776
8777   return chunk_size;
8778 }
8779
8780 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8781 {
8782   struct ElementInfo *ei = &element_info[element];
8783   struct ElementGroupInfo *group = ei->group;
8784   int chunk_size = 0;
8785   int i;
8786
8787   chunk_size += putFile16BitBE(file, element);
8788
8789   xx_ei = *ei;          // copy element data into temporary buffer
8790   xx_group = *group;    // copy group data into temporary buffer
8791
8792   // set default description string for this specific element
8793   strcpy(xx_default_description, getDefaultElementDescription(ei));
8794
8795   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8796     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8797
8798   return chunk_size;
8799 }
8800
8801 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8802 {
8803   struct ElementInfo *ei = &element_info[element];
8804   int chunk_size = 0;
8805   int i;
8806
8807   chunk_size += putFile16BitBE(file, element);
8808
8809   xx_ei = *ei;          // copy element data into temporary buffer
8810
8811   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8812     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8813
8814   return chunk_size;
8815 }
8816
8817 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8818                                   boolean save_as_template)
8819 {
8820   int chunk_size;
8821   int i;
8822   FILE *file;
8823
8824   if (!(file = fopen(filename, MODE_WRITE)))
8825   {
8826     Warn("cannot save level file '%s'", filename);
8827
8828     return;
8829   }
8830
8831   level->file_version = FILE_VERSION_ACTUAL;
8832   level->game_version = GAME_VERSION_ACTUAL;
8833
8834   level->creation_date = getCurrentDate();
8835
8836   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8837   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8838
8839   chunk_size = SaveLevel_VERS(NULL, level);
8840   putFileChunkBE(file, "VERS", chunk_size);
8841   SaveLevel_VERS(file, level);
8842
8843   chunk_size = SaveLevel_DATE(NULL, level);
8844   putFileChunkBE(file, "DATE", chunk_size);
8845   SaveLevel_DATE(file, level);
8846
8847   chunk_size = SaveLevel_NAME(NULL, level);
8848   putFileChunkBE(file, "NAME", chunk_size);
8849   SaveLevel_NAME(file, level);
8850
8851   chunk_size = SaveLevel_AUTH(NULL, level);
8852   putFileChunkBE(file, "AUTH", chunk_size);
8853   SaveLevel_AUTH(file, level);
8854
8855   chunk_size = SaveLevel_INFO(NULL, level);
8856   putFileChunkBE(file, "INFO", chunk_size);
8857   SaveLevel_INFO(file, level);
8858
8859   chunk_size = SaveLevel_BODY(NULL, level);
8860   putFileChunkBE(file, "BODY", chunk_size);
8861   SaveLevel_BODY(file, level);
8862
8863   chunk_size = SaveLevel_ELEM(NULL, level);
8864   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8865   {
8866     putFileChunkBE(file, "ELEM", chunk_size);
8867     SaveLevel_ELEM(file, level);
8868   }
8869
8870   for (i = 0; i < NUM_ENVELOPES; i++)
8871   {
8872     int element = EL_ENVELOPE_1 + i;
8873
8874     chunk_size = SaveLevel_NOTE(NULL, level, element);
8875     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8876     {
8877       putFileChunkBE(file, "NOTE", chunk_size);
8878       SaveLevel_NOTE(file, level, element);
8879     }
8880   }
8881
8882   // if not using template level, check for non-default custom/group elements
8883   if (!level->use_custom_template || save_as_template)
8884   {
8885     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8886     {
8887       int element = EL_CUSTOM_START + i;
8888
8889       chunk_size = SaveLevel_CUSX(NULL, level, element);
8890       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8891       {
8892         putFileChunkBE(file, "CUSX", chunk_size);
8893         SaveLevel_CUSX(file, level, element);
8894       }
8895     }
8896
8897     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8898     {
8899       int element = EL_GROUP_START + i;
8900
8901       chunk_size = SaveLevel_GRPX(NULL, level, element);
8902       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8903       {
8904         putFileChunkBE(file, "GRPX", chunk_size);
8905         SaveLevel_GRPX(file, level, element);
8906       }
8907     }
8908
8909     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8910     {
8911       int element = GET_EMPTY_ELEMENT(i);
8912
8913       chunk_size = SaveLevel_EMPX(NULL, level, element);
8914       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8915       {
8916         putFileChunkBE(file, "EMPX", chunk_size);
8917         SaveLevel_EMPX(file, level, element);
8918       }
8919     }
8920   }
8921
8922   fclose(file);
8923
8924   SetFilePermissions(filename, PERMS_PRIVATE);
8925 }
8926
8927 void SaveLevel(int nr)
8928 {
8929   char *filename = getDefaultLevelFilename(nr);
8930
8931   SaveLevelFromFilename(&level, filename, FALSE);
8932 }
8933
8934 void SaveLevelTemplate(void)
8935 {
8936   char *filename = getLocalLevelTemplateFilename();
8937
8938   SaveLevelFromFilename(&level, filename, TRUE);
8939 }
8940
8941 boolean SaveLevelChecked(int nr)
8942 {
8943   char *filename = getDefaultLevelFilename(nr);
8944   boolean new_level = !fileExists(filename);
8945   boolean level_saved = FALSE;
8946
8947   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8948   {
8949     SaveLevel(nr);
8950
8951     if (new_level)
8952       Request("Level saved!", REQ_CONFIRM);
8953
8954     level_saved = TRUE;
8955   }
8956
8957   return level_saved;
8958 }
8959
8960 void DumpLevel(struct LevelInfo *level)
8961 {
8962   if (level->no_level_file || level->no_valid_file)
8963   {
8964     Warn("cannot dump -- no valid level file found");
8965
8966     return;
8967   }
8968
8969   PrintLine("-", 79);
8970   Print("Level xxx (file version %08d, game version %08d)\n",
8971         level->file_version, level->game_version);
8972   PrintLine("-", 79);
8973
8974   Print("Level author: '%s'\n", level->author);
8975   Print("Level title:  '%s'\n", level->name);
8976   Print("\n");
8977   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8978   Print("\n");
8979   Print("Level time:  %d seconds\n", level->time);
8980   Print("Gems needed: %d\n", level->gems_needed);
8981   Print("\n");
8982   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8983   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8984   Print("Time for light:      %d seconds\n", level->time_light);
8985   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8986   Print("\n");
8987   Print("Amoeba speed: %d\n", level->amoeba_speed);
8988   Print("\n");
8989
8990   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8991   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8992   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8993   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8994   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8995   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8996
8997   if (options.debug)
8998   {
8999     int i, j;
9000
9001     for (i = 0; i < NUM_ENVELOPES; i++)
9002     {
9003       char *text = level->envelope[i].text;
9004       int text_len = strlen(text);
9005       boolean has_text = FALSE;
9006
9007       for (j = 0; j < text_len; j++)
9008         if (text[j] != ' ' && text[j] != '\n')
9009           has_text = TRUE;
9010
9011       if (has_text)
9012       {
9013         Print("\n");
9014         Print("Envelope %d:\n'%s'\n", i + 1, text);
9015       }
9016     }
9017   }
9018
9019   PrintLine("-", 79);
9020 }
9021
9022 void DumpLevels(void)
9023 {
9024   static LevelDirTree *dumplevel_leveldir = NULL;
9025
9026   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9027                                                  global.dumplevel_leveldir);
9028
9029   if (dumplevel_leveldir == NULL)
9030     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9031
9032   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9033       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9034     Fail("no such level number: %d", global.dumplevel_level_nr);
9035
9036   leveldir_current = dumplevel_leveldir;
9037
9038   LoadLevel(global.dumplevel_level_nr);
9039   DumpLevel(&level);
9040
9041   CloseAllAndExit(0);
9042 }
9043
9044
9045 // ============================================================================
9046 // tape file functions
9047 // ============================================================================
9048
9049 static void setTapeInfoToDefaults(void)
9050 {
9051   int i;
9052
9053   // always start with reliable default values (empty tape)
9054   TapeErase();
9055
9056   // default values (also for pre-1.2 tapes) with only the first player
9057   tape.player_participates[0] = TRUE;
9058   for (i = 1; i < MAX_PLAYERS; i++)
9059     tape.player_participates[i] = FALSE;
9060
9061   // at least one (default: the first) player participates in every tape
9062   tape.num_participating_players = 1;
9063
9064   tape.property_bits = TAPE_PROPERTY_NONE;
9065
9066   tape.level_nr = level_nr;
9067   tape.counter = 0;
9068   tape.changed = FALSE;
9069   tape.solved = FALSE;
9070
9071   tape.recording = FALSE;
9072   tape.playing = FALSE;
9073   tape.pausing = FALSE;
9074
9075   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9076   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9077
9078   tape.no_info_chunk = TRUE;
9079   tape.no_valid_file = FALSE;
9080 }
9081
9082 static int getTapePosSize(struct TapeInfo *tape)
9083 {
9084   int tape_pos_size = 0;
9085
9086   if (tape->use_key_actions)
9087     tape_pos_size += tape->num_participating_players;
9088
9089   if (tape->use_mouse_actions)
9090     tape_pos_size += 3;         // x and y position and mouse button mask
9091
9092   tape_pos_size += 1;           // tape action delay value
9093
9094   return tape_pos_size;
9095 }
9096
9097 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9098 {
9099   tape->use_key_actions = FALSE;
9100   tape->use_mouse_actions = FALSE;
9101
9102   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9103     tape->use_key_actions = TRUE;
9104
9105   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9106     tape->use_mouse_actions = TRUE;
9107 }
9108
9109 static int getTapeActionValue(struct TapeInfo *tape)
9110 {
9111   return (tape->use_key_actions &&
9112           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9113           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9114           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9115           TAPE_ACTIONS_DEFAULT);
9116 }
9117
9118 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9119 {
9120   tape->file_version = getFileVersion(file);
9121   tape->game_version = getFileVersion(file);
9122
9123   return chunk_size;
9124 }
9125
9126 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9127 {
9128   int i;
9129
9130   tape->random_seed = getFile32BitBE(file);
9131   tape->date        = getFile32BitBE(file);
9132   tape->length      = getFile32BitBE(file);
9133
9134   // read header fields that are new since version 1.2
9135   if (tape->file_version >= FILE_VERSION_1_2)
9136   {
9137     byte store_participating_players = getFile8Bit(file);
9138     int engine_version;
9139
9140     // since version 1.2, tapes store which players participate in the tape
9141     tape->num_participating_players = 0;
9142     for (i = 0; i < MAX_PLAYERS; i++)
9143     {
9144       tape->player_participates[i] = FALSE;
9145
9146       if (store_participating_players & (1 << i))
9147       {
9148         tape->player_participates[i] = TRUE;
9149         tape->num_participating_players++;
9150       }
9151     }
9152
9153     setTapeActionFlags(tape, getFile8Bit(file));
9154
9155     tape->property_bits = getFile8Bit(file);
9156     tape->solved = getFile8Bit(file);
9157
9158     engine_version = getFileVersion(file);
9159     if (engine_version > 0)
9160       tape->engine_version = engine_version;
9161     else
9162       tape->engine_version = tape->game_version;
9163   }
9164
9165   return chunk_size;
9166 }
9167
9168 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9169 {
9170   tape->scr_fieldx = getFile8Bit(file);
9171   tape->scr_fieldy = getFile8Bit(file);
9172
9173   return chunk_size;
9174 }
9175
9176 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9177 {
9178   char *level_identifier = NULL;
9179   int level_identifier_size;
9180   int i;
9181
9182   tape->no_info_chunk = FALSE;
9183
9184   level_identifier_size = getFile16BitBE(file);
9185
9186   level_identifier = checked_malloc(level_identifier_size);
9187
9188   for (i = 0; i < level_identifier_size; i++)
9189     level_identifier[i] = getFile8Bit(file);
9190
9191   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9192   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9193
9194   checked_free(level_identifier);
9195
9196   tape->level_nr = getFile16BitBE(file);
9197
9198   chunk_size = 2 + level_identifier_size + 2;
9199
9200   return chunk_size;
9201 }
9202
9203 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9204 {
9205   int i, j;
9206   int tape_pos_size = getTapePosSize(tape);
9207   int chunk_size_expected = tape_pos_size * tape->length;
9208
9209   if (chunk_size_expected != chunk_size)
9210   {
9211     ReadUnusedBytesFromFile(file, chunk_size);
9212     return chunk_size_expected;
9213   }
9214
9215   for (i = 0; i < tape->length; i++)
9216   {
9217     if (i >= MAX_TAPE_LEN)
9218     {
9219       Warn("tape truncated -- size exceeds maximum tape size %d",
9220             MAX_TAPE_LEN);
9221
9222       // tape too large; read and ignore remaining tape data from this chunk
9223       for (;i < tape->length; i++)
9224         ReadUnusedBytesFromFile(file, tape_pos_size);
9225
9226       break;
9227     }
9228
9229     if (tape->use_key_actions)
9230     {
9231       for (j = 0; j < MAX_PLAYERS; j++)
9232       {
9233         tape->pos[i].action[j] = MV_NONE;
9234
9235         if (tape->player_participates[j])
9236           tape->pos[i].action[j] = getFile8Bit(file);
9237       }
9238     }
9239
9240     if (tape->use_mouse_actions)
9241     {
9242       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9243       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9244       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9245     }
9246
9247     tape->pos[i].delay = getFile8Bit(file);
9248
9249     if (tape->file_version == FILE_VERSION_1_0)
9250     {
9251       // eliminate possible diagonal moves in old tapes
9252       // this is only for backward compatibility
9253
9254       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9255       byte action = tape->pos[i].action[0];
9256       int k, num_moves = 0;
9257
9258       for (k = 0; k < 4; k++)
9259       {
9260         if (action & joy_dir[k])
9261         {
9262           tape->pos[i + num_moves].action[0] = joy_dir[k];
9263           if (num_moves > 0)
9264             tape->pos[i + num_moves].delay = 0;
9265           num_moves++;
9266         }
9267       }
9268
9269       if (num_moves > 1)
9270       {
9271         num_moves--;
9272         i += num_moves;
9273         tape->length += num_moves;
9274       }
9275     }
9276     else if (tape->file_version < FILE_VERSION_2_0)
9277     {
9278       // convert pre-2.0 tapes to new tape format
9279
9280       if (tape->pos[i].delay > 1)
9281       {
9282         // action part
9283         tape->pos[i + 1] = tape->pos[i];
9284         tape->pos[i + 1].delay = 1;
9285
9286         // delay part
9287         for (j = 0; j < MAX_PLAYERS; j++)
9288           tape->pos[i].action[j] = MV_NONE;
9289         tape->pos[i].delay--;
9290
9291         i++;
9292         tape->length++;
9293       }
9294     }
9295
9296     if (checkEndOfFile(file))
9297       break;
9298   }
9299
9300   if (i != tape->length)
9301     chunk_size = tape_pos_size * i;
9302
9303   return chunk_size;
9304 }
9305
9306 static void LoadTape_SokobanSolution(char *filename)
9307 {
9308   File *file;
9309   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9310
9311   if (!(file = openFile(filename, MODE_READ)))
9312   {
9313     tape.no_valid_file = TRUE;
9314
9315     return;
9316   }
9317
9318   while (!checkEndOfFile(file))
9319   {
9320     unsigned char c = getByteFromFile(file);
9321
9322     if (checkEndOfFile(file))
9323       break;
9324
9325     switch (c)
9326     {
9327       case 'u':
9328       case 'U':
9329         tape.pos[tape.length].action[0] = MV_UP;
9330         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9331         tape.length++;
9332         break;
9333
9334       case 'd':
9335       case 'D':
9336         tape.pos[tape.length].action[0] = MV_DOWN;
9337         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9338         tape.length++;
9339         break;
9340
9341       case 'l':
9342       case 'L':
9343         tape.pos[tape.length].action[0] = MV_LEFT;
9344         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9345         tape.length++;
9346         break;
9347
9348       case 'r':
9349       case 'R':
9350         tape.pos[tape.length].action[0] = MV_RIGHT;
9351         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9352         tape.length++;
9353         break;
9354
9355       case '\n':
9356       case '\r':
9357       case '\t':
9358       case ' ':
9359         // ignore white-space characters
9360         break;
9361
9362       default:
9363         tape.no_valid_file = TRUE;
9364
9365         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9366
9367         break;
9368     }
9369   }
9370
9371   closeFile(file);
9372
9373   if (tape.no_valid_file)
9374     return;
9375
9376   tape.length_frames  = GetTapeLengthFrames();
9377   tape.length_seconds = GetTapeLengthSeconds();
9378 }
9379
9380 void LoadTapeFromFilename(char *filename)
9381 {
9382   char cookie[MAX_LINE_LEN];
9383   char chunk_name[CHUNK_ID_LEN + 1];
9384   File *file;
9385   int chunk_size;
9386
9387   // always start with reliable default values
9388   setTapeInfoToDefaults();
9389
9390   if (strSuffix(filename, ".sln"))
9391   {
9392     LoadTape_SokobanSolution(filename);
9393
9394     return;
9395   }
9396
9397   if (!(file = openFile(filename, MODE_READ)))
9398   {
9399     tape.no_valid_file = TRUE;
9400
9401     return;
9402   }
9403
9404   getFileChunkBE(file, chunk_name, NULL);
9405   if (strEqual(chunk_name, "RND1"))
9406   {
9407     getFile32BitBE(file);               // not used
9408
9409     getFileChunkBE(file, chunk_name, NULL);
9410     if (!strEqual(chunk_name, "TAPE"))
9411     {
9412       tape.no_valid_file = TRUE;
9413
9414       Warn("unknown format of tape file '%s'", filename);
9415
9416       closeFile(file);
9417
9418       return;
9419     }
9420   }
9421   else  // check for pre-2.0 file format with cookie string
9422   {
9423     strcpy(cookie, chunk_name);
9424     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9425       cookie[4] = '\0';
9426     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9427       cookie[strlen(cookie) - 1] = '\0';
9428
9429     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9430     {
9431       tape.no_valid_file = TRUE;
9432
9433       Warn("unknown format of tape file '%s'", filename);
9434
9435       closeFile(file);
9436
9437       return;
9438     }
9439
9440     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9441     {
9442       tape.no_valid_file = TRUE;
9443
9444       Warn("unsupported version of tape file '%s'", filename);
9445
9446       closeFile(file);
9447
9448       return;
9449     }
9450
9451     // pre-2.0 tape files have no game version, so use file version here
9452     tape.game_version = tape.file_version;
9453   }
9454
9455   if (tape.file_version < FILE_VERSION_1_2)
9456   {
9457     // tape files from versions before 1.2.0 without chunk structure
9458     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9459     LoadTape_BODY(file, 2 * tape.length,      &tape);
9460   }
9461   else
9462   {
9463     static struct
9464     {
9465       char *name;
9466       int size;
9467       int (*loader)(File *, int, struct TapeInfo *);
9468     }
9469     chunk_info[] =
9470     {
9471       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9472       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9473       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9474       { "INFO", -1,                     LoadTape_INFO },
9475       { "BODY", -1,                     LoadTape_BODY },
9476       {  NULL,  0,                      NULL }
9477     };
9478
9479     while (getFileChunkBE(file, chunk_name, &chunk_size))
9480     {
9481       int i = 0;
9482
9483       while (chunk_info[i].name != NULL &&
9484              !strEqual(chunk_name, chunk_info[i].name))
9485         i++;
9486
9487       if (chunk_info[i].name == NULL)
9488       {
9489         Warn("unknown chunk '%s' in tape file '%s'",
9490               chunk_name, filename);
9491
9492         ReadUnusedBytesFromFile(file, chunk_size);
9493       }
9494       else if (chunk_info[i].size != -1 &&
9495                chunk_info[i].size != chunk_size)
9496       {
9497         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9498               chunk_size, chunk_name, filename);
9499
9500         ReadUnusedBytesFromFile(file, chunk_size);
9501       }
9502       else
9503       {
9504         // call function to load this tape chunk
9505         int chunk_size_expected =
9506           (chunk_info[i].loader)(file, chunk_size, &tape);
9507
9508         // the size of some chunks cannot be checked before reading other
9509         // chunks first (like "HEAD" and "BODY") that contain some header
9510         // information, so check them here
9511         if (chunk_size_expected != chunk_size)
9512         {
9513           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9514                 chunk_size, chunk_name, filename);
9515         }
9516       }
9517     }
9518   }
9519
9520   closeFile(file);
9521
9522   tape.length_frames  = GetTapeLengthFrames();
9523   tape.length_seconds = GetTapeLengthSeconds();
9524
9525 #if 0
9526   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9527         tape.file_version);
9528   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9529         tape.game_version);
9530   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9531         tape.engine_version);
9532 #endif
9533 }
9534
9535 void LoadTape(int nr)
9536 {
9537   char *filename = getTapeFilename(nr);
9538
9539   LoadTapeFromFilename(filename);
9540 }
9541
9542 void LoadSolutionTape(int nr)
9543 {
9544   char *filename = getSolutionTapeFilename(nr);
9545
9546   LoadTapeFromFilename(filename);
9547
9548   if (TAPE_IS_EMPTY(tape))
9549   {
9550     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9551         level.native_bd_level->replay != NULL)
9552       CopyNativeTape_BD_to_RND(&level);
9553     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9554         level.native_sp_level->demo.is_available)
9555       CopyNativeTape_SP_to_RND(&level);
9556   }
9557 }
9558
9559 void LoadScoreTape(char *score_tape_basename, int nr)
9560 {
9561   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9562
9563   LoadTapeFromFilename(filename);
9564 }
9565
9566 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9567 {
9568   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9569
9570   LoadTapeFromFilename(filename);
9571 }
9572
9573 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9574 {
9575   // chunk required for team mode tapes with non-default screen size
9576   return (tape->num_participating_players > 1 &&
9577           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9578            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9579 }
9580
9581 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9582 {
9583   putFileVersion(file, tape->file_version);
9584   putFileVersion(file, tape->game_version);
9585 }
9586
9587 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9588 {
9589   int i;
9590   byte store_participating_players = 0;
9591
9592   // set bits for participating players for compact storage
9593   for (i = 0; i < MAX_PLAYERS; i++)
9594     if (tape->player_participates[i])
9595       store_participating_players |= (1 << i);
9596
9597   putFile32BitBE(file, tape->random_seed);
9598   putFile32BitBE(file, tape->date);
9599   putFile32BitBE(file, tape->length);
9600
9601   putFile8Bit(file, store_participating_players);
9602
9603   putFile8Bit(file, getTapeActionValue(tape));
9604
9605   putFile8Bit(file, tape->property_bits);
9606   putFile8Bit(file, tape->solved);
9607
9608   putFileVersion(file, tape->engine_version);
9609 }
9610
9611 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9612 {
9613   putFile8Bit(file, tape->scr_fieldx);
9614   putFile8Bit(file, tape->scr_fieldy);
9615 }
9616
9617 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9618 {
9619   int level_identifier_size = strlen(tape->level_identifier) + 1;
9620   int i;
9621
9622   putFile16BitBE(file, level_identifier_size);
9623
9624   for (i = 0; i < level_identifier_size; i++)
9625     putFile8Bit(file, tape->level_identifier[i]);
9626
9627   putFile16BitBE(file, tape->level_nr);
9628 }
9629
9630 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9631 {
9632   int i, j;
9633
9634   for (i = 0; i < tape->length; i++)
9635   {
9636     if (tape->use_key_actions)
9637     {
9638       for (j = 0; j < MAX_PLAYERS; j++)
9639         if (tape->player_participates[j])
9640           putFile8Bit(file, tape->pos[i].action[j]);
9641     }
9642
9643     if (tape->use_mouse_actions)
9644     {
9645       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9646       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9647       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9648     }
9649
9650     putFile8Bit(file, tape->pos[i].delay);
9651   }
9652 }
9653
9654 void SaveTapeToFilename(char *filename)
9655 {
9656   FILE *file;
9657   int tape_pos_size;
9658   int info_chunk_size;
9659   int body_chunk_size;
9660
9661   if (!(file = fopen(filename, MODE_WRITE)))
9662   {
9663     Warn("cannot save level recording file '%s'", filename);
9664
9665     return;
9666   }
9667
9668   tape_pos_size = getTapePosSize(&tape);
9669
9670   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9671   body_chunk_size = tape_pos_size * tape.length;
9672
9673   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9674   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9675
9676   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9677   SaveTape_VERS(file, &tape);
9678
9679   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9680   SaveTape_HEAD(file, &tape);
9681
9682   if (checkSaveTape_SCRN(&tape))
9683   {
9684     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9685     SaveTape_SCRN(file, &tape);
9686   }
9687
9688   putFileChunkBE(file, "INFO", info_chunk_size);
9689   SaveTape_INFO(file, &tape);
9690
9691   putFileChunkBE(file, "BODY", body_chunk_size);
9692   SaveTape_BODY(file, &tape);
9693
9694   fclose(file);
9695
9696   SetFilePermissions(filename, PERMS_PRIVATE);
9697 }
9698
9699 static void SaveTapeExt(char *filename)
9700 {
9701   int i;
9702
9703   tape.file_version = FILE_VERSION_ACTUAL;
9704   tape.game_version = GAME_VERSION_ACTUAL;
9705
9706   tape.num_participating_players = 0;
9707
9708   // count number of participating players
9709   for (i = 0; i < MAX_PLAYERS; i++)
9710     if (tape.player_participates[i])
9711       tape.num_participating_players++;
9712
9713   SaveTapeToFilename(filename);
9714
9715   tape.changed = FALSE;
9716 }
9717
9718 void SaveTape(int nr)
9719 {
9720   char *filename = getTapeFilename(nr);
9721
9722   InitTapeDirectory(leveldir_current->subdir);
9723
9724   SaveTapeExt(filename);
9725 }
9726
9727 void SaveScoreTape(int nr)
9728 {
9729   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9730
9731   // used instead of "leveldir_current->subdir" (for network games)
9732   InitScoreTapeDirectory(levelset.identifier, nr);
9733
9734   SaveTapeExt(filename);
9735 }
9736
9737 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9738                                   unsigned int req_state_added)
9739 {
9740   char *filename = getTapeFilename(nr);
9741   boolean new_tape = !fileExists(filename);
9742   boolean tape_saved = FALSE;
9743
9744   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9745   {
9746     SaveTape(nr);
9747
9748     if (new_tape)
9749       Request(msg_saved, REQ_CONFIRM | req_state_added);
9750
9751     tape_saved = TRUE;
9752   }
9753
9754   return tape_saved;
9755 }
9756
9757 boolean SaveTapeChecked(int nr)
9758 {
9759   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9760 }
9761
9762 boolean SaveTapeChecked_LevelSolved(int nr)
9763 {
9764   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9765                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9766 }
9767
9768 void DumpTape(struct TapeInfo *tape)
9769 {
9770   int tape_frame_counter;
9771   int i, j;
9772
9773   if (tape->no_valid_file)
9774   {
9775     Warn("cannot dump -- no valid tape file found");
9776
9777     return;
9778   }
9779
9780   PrintLine("-", 79);
9781
9782   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9783         tape->level_nr, tape->file_version, tape->game_version);
9784   Print("                  (effective engine version %08d)\n",
9785         tape->engine_version);
9786   Print("Level series identifier: '%s'\n", tape->level_identifier);
9787
9788   Print("Solution tape: %s\n",
9789         tape->solved ? "yes" :
9790         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9791
9792   Print("Special tape properties: ");
9793   if (tape->property_bits == TAPE_PROPERTY_NONE)
9794     Print("[none]");
9795   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9796     Print("[em_random_bug]");
9797   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9798     Print("[game_speed]");
9799   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9800     Print("[pause]");
9801   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9802     Print("[single_step]");
9803   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9804     Print("[snapshot]");
9805   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9806     Print("[replayed]");
9807   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9808     Print("[tas_keys]");
9809   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9810     Print("[small_graphics]");
9811   Print("\n");
9812
9813   int year2 = tape->date / 10000;
9814   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9815   int month_index_raw = (tape->date / 100) % 100;
9816   int month_index = month_index_raw % 12;       // prevent invalid index
9817   int month = month_index + 1;
9818   int day = tape->date % 100;
9819
9820   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9821
9822   PrintLine("-", 79);
9823
9824   tape_frame_counter = 0;
9825
9826   for (i = 0; i < tape->length; i++)
9827   {
9828     if (i >= MAX_TAPE_LEN)
9829       break;
9830
9831     Print("%04d: ", i);
9832
9833     for (j = 0; j < MAX_PLAYERS; j++)
9834     {
9835       if (tape->player_participates[j])
9836       {
9837         int action = tape->pos[i].action[j];
9838
9839         Print("%d:%02x ", j, action);
9840         Print("[%c%c%c%c|%c%c] - ",
9841               (action & JOY_LEFT ? '<' : ' '),
9842               (action & JOY_RIGHT ? '>' : ' '),
9843               (action & JOY_UP ? '^' : ' '),
9844               (action & JOY_DOWN ? 'v' : ' '),
9845               (action & JOY_BUTTON_1 ? '1' : ' '),
9846               (action & JOY_BUTTON_2 ? '2' : ' '));
9847       }
9848     }
9849
9850     Print("(%03d) ", tape->pos[i].delay);
9851     Print("[%05d]\n", tape_frame_counter);
9852
9853     tape_frame_counter += tape->pos[i].delay;
9854   }
9855
9856   PrintLine("-", 79);
9857 }
9858
9859 void DumpTapes(void)
9860 {
9861   static LevelDirTree *dumptape_leveldir = NULL;
9862
9863   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9864                                                 global.dumptape_leveldir);
9865
9866   if (dumptape_leveldir == NULL)
9867     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9868
9869   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9870       global.dumptape_level_nr > dumptape_leveldir->last_level)
9871     Fail("no such level number: %d", global.dumptape_level_nr);
9872
9873   leveldir_current = dumptape_leveldir;
9874
9875   if (options.mytapes)
9876     LoadTape(global.dumptape_level_nr);
9877   else
9878     LoadSolutionTape(global.dumptape_level_nr);
9879
9880   DumpTape(&tape);
9881
9882   CloseAllAndExit(0);
9883 }
9884
9885
9886 // ============================================================================
9887 // score file functions
9888 // ============================================================================
9889
9890 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9891 {
9892   int i;
9893
9894   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9895   {
9896     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9897     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9898     scores->entry[i].score = 0;
9899     scores->entry[i].time = 0;
9900
9901     scores->entry[i].id = -1;
9902     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9903     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9904     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9905     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9906     strcpy(scores->entry[i].country_code, "??");
9907   }
9908
9909   scores->num_entries = 0;
9910   scores->last_added = -1;
9911   scores->last_added_local = -1;
9912
9913   scores->updated = FALSE;
9914   scores->uploaded = FALSE;
9915   scores->tape_downloaded = FALSE;
9916   scores->force_last_added = FALSE;
9917
9918   // The following values are intentionally not reset here:
9919   // - last_level_nr
9920   // - last_entry_nr
9921   // - next_level_nr
9922   // - continue_playing
9923   // - continue_on_return
9924 }
9925
9926 static void setScoreInfoToDefaults(void)
9927 {
9928   setScoreInfoToDefaultsExt(&scores);
9929 }
9930
9931 static void setServerScoreInfoToDefaults(void)
9932 {
9933   setScoreInfoToDefaultsExt(&server_scores);
9934 }
9935
9936 static void LoadScore_OLD(int nr)
9937 {
9938   int i;
9939   char *filename = getScoreFilename(nr);
9940   char cookie[MAX_LINE_LEN];
9941   char line[MAX_LINE_LEN];
9942   char *line_ptr;
9943   FILE *file;
9944
9945   if (!(file = fopen(filename, MODE_READ)))
9946     return;
9947
9948   // check file identifier
9949   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9950     cookie[0] = '\0';
9951   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9952     cookie[strlen(cookie) - 1] = '\0';
9953
9954   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9955   {
9956     Warn("unknown format of score file '%s'", filename);
9957
9958     fclose(file);
9959
9960     return;
9961   }
9962
9963   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9964   {
9965     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9966       Warn("fscanf() failed; %s", strerror(errno));
9967
9968     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9969       line[0] = '\0';
9970
9971     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9972       line[strlen(line) - 1] = '\0';
9973
9974     for (line_ptr = line; *line_ptr; line_ptr++)
9975     {
9976       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9977       {
9978         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9979         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9980         break;
9981       }
9982     }
9983   }
9984
9985   fclose(file);
9986 }
9987
9988 static void ConvertScore_OLD(void)
9989 {
9990   // only convert score to time for levels that rate playing time over score
9991   if (!level.rate_time_over_score)
9992     return;
9993
9994   // convert old score to playing time for score-less levels (like Supaplex)
9995   int time_final_max = 999;
9996   int i;
9997
9998   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9999   {
10000     int score = scores.entry[i].score;
10001
10002     if (score > 0 && score < time_final_max)
10003       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
10004   }
10005 }
10006
10007 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10008 {
10009   scores->file_version = getFileVersion(file);
10010   scores->game_version = getFileVersion(file);
10011
10012   return chunk_size;
10013 }
10014
10015 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10016 {
10017   char *level_identifier = NULL;
10018   int level_identifier_size;
10019   int i;
10020
10021   level_identifier_size = getFile16BitBE(file);
10022
10023   level_identifier = checked_malloc(level_identifier_size);
10024
10025   for (i = 0; i < level_identifier_size; i++)
10026     level_identifier[i] = getFile8Bit(file);
10027
10028   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10029   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10030
10031   checked_free(level_identifier);
10032
10033   scores->level_nr = getFile16BitBE(file);
10034   scores->num_entries = getFile16BitBE(file);
10035
10036   chunk_size = 2 + level_identifier_size + 2 + 2;
10037
10038   return chunk_size;
10039 }
10040
10041 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10042 {
10043   int i, j;
10044
10045   for (i = 0; i < scores->num_entries; i++)
10046   {
10047     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10048       scores->entry[i].name[j] = getFile8Bit(file);
10049
10050     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10051   }
10052
10053   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10054
10055   return chunk_size;
10056 }
10057
10058 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10059 {
10060   int i;
10061
10062   for (i = 0; i < scores->num_entries; i++)
10063     scores->entry[i].score = getFile16BitBE(file);
10064
10065   chunk_size = scores->num_entries * 2;
10066
10067   return chunk_size;
10068 }
10069
10070 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10071 {
10072   int i;
10073
10074   for (i = 0; i < scores->num_entries; i++)
10075     scores->entry[i].score = getFile32BitBE(file);
10076
10077   chunk_size = scores->num_entries * 4;
10078
10079   return chunk_size;
10080 }
10081
10082 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10083 {
10084   int i;
10085
10086   for (i = 0; i < scores->num_entries; i++)
10087     scores->entry[i].time = getFile32BitBE(file);
10088
10089   chunk_size = scores->num_entries * 4;
10090
10091   return chunk_size;
10092 }
10093
10094 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10095 {
10096   int i, j;
10097
10098   for (i = 0; i < scores->num_entries; i++)
10099   {
10100     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10101       scores->entry[i].tape_basename[j] = getFile8Bit(file);
10102
10103     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10104   }
10105
10106   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10107
10108   return chunk_size;
10109 }
10110
10111 void LoadScore(int nr)
10112 {
10113   char *filename = getScoreFilename(nr);
10114   char cookie[MAX_LINE_LEN];
10115   char chunk_name[CHUNK_ID_LEN + 1];
10116   int chunk_size;
10117   boolean old_score_file_format = FALSE;
10118   File *file;
10119
10120   // always start with reliable default values
10121   setScoreInfoToDefaults();
10122
10123   if (!(file = openFile(filename, MODE_READ)))
10124     return;
10125
10126   getFileChunkBE(file, chunk_name, NULL);
10127   if (strEqual(chunk_name, "RND1"))
10128   {
10129     getFile32BitBE(file);               // not used
10130
10131     getFileChunkBE(file, chunk_name, NULL);
10132     if (!strEqual(chunk_name, "SCOR"))
10133     {
10134       Warn("unknown format of score file '%s'", filename);
10135
10136       closeFile(file);
10137
10138       return;
10139     }
10140   }
10141   else  // check for old file format with cookie string
10142   {
10143     strcpy(cookie, chunk_name);
10144     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10145       cookie[4] = '\0';
10146     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10147       cookie[strlen(cookie) - 1] = '\0';
10148
10149     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10150     {
10151       Warn("unknown format of score file '%s'", filename);
10152
10153       closeFile(file);
10154
10155       return;
10156     }
10157
10158     old_score_file_format = TRUE;
10159   }
10160
10161   if (old_score_file_format)
10162   {
10163     // score files from versions before 4.2.4.0 without chunk structure
10164     LoadScore_OLD(nr);
10165
10166     // convert score to time, if possible (mainly for Supaplex levels)
10167     ConvertScore_OLD();
10168   }
10169   else
10170   {
10171     static struct
10172     {
10173       char *name;
10174       int size;
10175       int (*loader)(File *, int, struct ScoreInfo *);
10176     }
10177     chunk_info[] =
10178     {
10179       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10180       { "INFO", -1,                     LoadScore_INFO },
10181       { "NAME", -1,                     LoadScore_NAME },
10182       { "SCOR", -1,                     LoadScore_SCOR },
10183       { "SC4R", -1,                     LoadScore_SC4R },
10184       { "TIME", -1,                     LoadScore_TIME },
10185       { "TAPE", -1,                     LoadScore_TAPE },
10186
10187       {  NULL,  0,                      NULL }
10188     };
10189
10190     while (getFileChunkBE(file, chunk_name, &chunk_size))
10191     {
10192       int i = 0;
10193
10194       while (chunk_info[i].name != NULL &&
10195              !strEqual(chunk_name, chunk_info[i].name))
10196         i++;
10197
10198       if (chunk_info[i].name == NULL)
10199       {
10200         Warn("unknown chunk '%s' in score file '%s'",
10201               chunk_name, filename);
10202
10203         ReadUnusedBytesFromFile(file, chunk_size);
10204       }
10205       else if (chunk_info[i].size != -1 &&
10206                chunk_info[i].size != chunk_size)
10207       {
10208         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10209               chunk_size, chunk_name, filename);
10210
10211         ReadUnusedBytesFromFile(file, chunk_size);
10212       }
10213       else
10214       {
10215         // call function to load this score chunk
10216         int chunk_size_expected =
10217           (chunk_info[i].loader)(file, chunk_size, &scores);
10218
10219         // the size of some chunks cannot be checked before reading other
10220         // chunks first (like "HEAD" and "BODY") that contain some header
10221         // information, so check them here
10222         if (chunk_size_expected != chunk_size)
10223         {
10224           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10225                 chunk_size, chunk_name, filename);
10226         }
10227       }
10228     }
10229   }
10230
10231   closeFile(file);
10232 }
10233
10234 #if ENABLE_HISTORIC_CHUNKS
10235 void SaveScore_OLD(int nr)
10236 {
10237   int i;
10238   char *filename = getScoreFilename(nr);
10239   FILE *file;
10240
10241   // used instead of "leveldir_current->subdir" (for network games)
10242   InitScoreDirectory(levelset.identifier);
10243
10244   if (!(file = fopen(filename, MODE_WRITE)))
10245   {
10246     Warn("cannot save score for level %d", nr);
10247
10248     return;
10249   }
10250
10251   fprintf(file, "%s\n\n", SCORE_COOKIE);
10252
10253   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10254     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10255
10256   fclose(file);
10257
10258   SetFilePermissions(filename, PERMS_PRIVATE);
10259 }
10260 #endif
10261
10262 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10263 {
10264   putFileVersion(file, scores->file_version);
10265   putFileVersion(file, scores->game_version);
10266 }
10267
10268 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10269 {
10270   int level_identifier_size = strlen(scores->level_identifier) + 1;
10271   int i;
10272
10273   putFile16BitBE(file, level_identifier_size);
10274
10275   for (i = 0; i < level_identifier_size; i++)
10276     putFile8Bit(file, scores->level_identifier[i]);
10277
10278   putFile16BitBE(file, scores->level_nr);
10279   putFile16BitBE(file, scores->num_entries);
10280 }
10281
10282 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10283 {
10284   int i, j;
10285
10286   for (i = 0; i < scores->num_entries; i++)
10287   {
10288     int name_size = strlen(scores->entry[i].name);
10289
10290     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10291       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10292   }
10293 }
10294
10295 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10296 {
10297   int i;
10298
10299   for (i = 0; i < scores->num_entries; i++)
10300     putFile16BitBE(file, scores->entry[i].score);
10301 }
10302
10303 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10304 {
10305   int i;
10306
10307   for (i = 0; i < scores->num_entries; i++)
10308     putFile32BitBE(file, scores->entry[i].score);
10309 }
10310
10311 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10312 {
10313   int i;
10314
10315   for (i = 0; i < scores->num_entries; i++)
10316     putFile32BitBE(file, scores->entry[i].time);
10317 }
10318
10319 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10320 {
10321   int i, j;
10322
10323   for (i = 0; i < scores->num_entries; i++)
10324   {
10325     int size = strlen(scores->entry[i].tape_basename);
10326
10327     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10328       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10329   }
10330 }
10331
10332 static void SaveScoreToFilename(char *filename)
10333 {
10334   FILE *file;
10335   int info_chunk_size;
10336   int name_chunk_size;
10337   int scor_chunk_size;
10338   int sc4r_chunk_size;
10339   int time_chunk_size;
10340   int tape_chunk_size;
10341   boolean has_large_score_values;
10342   int i;
10343
10344   if (!(file = fopen(filename, MODE_WRITE)))
10345   {
10346     Warn("cannot save score file '%s'", filename);
10347
10348     return;
10349   }
10350
10351   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10352   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10353   scor_chunk_size = scores.num_entries * 2;
10354   sc4r_chunk_size = scores.num_entries * 4;
10355   time_chunk_size = scores.num_entries * 4;
10356   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10357
10358   has_large_score_values = FALSE;
10359   for (i = 0; i < scores.num_entries; i++)
10360     if (scores.entry[i].score > 0xffff)
10361       has_large_score_values = TRUE;
10362
10363   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10364   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10365
10366   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10367   SaveScore_VERS(file, &scores);
10368
10369   putFileChunkBE(file, "INFO", info_chunk_size);
10370   SaveScore_INFO(file, &scores);
10371
10372   putFileChunkBE(file, "NAME", name_chunk_size);
10373   SaveScore_NAME(file, &scores);
10374
10375   if (has_large_score_values)
10376   {
10377     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10378     SaveScore_SC4R(file, &scores);
10379   }
10380   else
10381   {
10382     putFileChunkBE(file, "SCOR", scor_chunk_size);
10383     SaveScore_SCOR(file, &scores);
10384   }
10385
10386   putFileChunkBE(file, "TIME", time_chunk_size);
10387   SaveScore_TIME(file, &scores);
10388
10389   putFileChunkBE(file, "TAPE", tape_chunk_size);
10390   SaveScore_TAPE(file, &scores);
10391
10392   fclose(file);
10393
10394   SetFilePermissions(filename, PERMS_PRIVATE);
10395 }
10396
10397 void SaveScore(int nr)
10398 {
10399   char *filename = getScoreFilename(nr);
10400   int i;
10401
10402   // used instead of "leveldir_current->subdir" (for network games)
10403   InitScoreDirectory(levelset.identifier);
10404
10405   scores.file_version = FILE_VERSION_ACTUAL;
10406   scores.game_version = GAME_VERSION_ACTUAL;
10407
10408   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10409   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10410   scores.level_nr = level_nr;
10411
10412   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10413     if (scores.entry[i].score == 0 &&
10414         scores.entry[i].time == 0 &&
10415         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10416       break;
10417
10418   scores.num_entries = i;
10419
10420   if (scores.num_entries == 0)
10421     return;
10422
10423   SaveScoreToFilename(filename);
10424 }
10425
10426 static void LoadServerScoreFromCache(int nr)
10427 {
10428   struct ScoreEntry score_entry;
10429   struct
10430   {
10431     void *value;
10432     boolean is_string;
10433     int string_size;
10434   }
10435   score_mapping[] =
10436   {
10437     { &score_entry.score,               FALSE,  0                       },
10438     { &score_entry.time,                FALSE,  0                       },
10439     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10440     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10441     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10442     { &score_entry.id,                  FALSE,  0                       },
10443     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10444     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10445     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10446     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10447
10448     { NULL,                             FALSE,  0                       }
10449   };
10450   char *filename = getScoreCacheFilename(nr);
10451   SetupFileHash *score_hash = loadSetupFileHash(filename);
10452   int i, j;
10453
10454   server_scores.num_entries = 0;
10455
10456   if (score_hash == NULL)
10457     return;
10458
10459   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10460   {
10461     score_entry = server_scores.entry[i];
10462
10463     for (j = 0; score_mapping[j].value != NULL; j++)
10464     {
10465       char token[10];
10466
10467       sprintf(token, "%02d.%d", i, j);
10468
10469       char *value = getHashEntry(score_hash, token);
10470
10471       if (value == NULL)
10472         continue;
10473
10474       if (score_mapping[j].is_string)
10475       {
10476         char *score_value = (char *)score_mapping[j].value;
10477         int value_size = score_mapping[j].string_size;
10478
10479         strncpy(score_value, value, value_size);
10480         score_value[value_size] = '\0';
10481       }
10482       else
10483       {
10484         int *score_value = (int *)score_mapping[j].value;
10485
10486         *score_value = atoi(value);
10487       }
10488
10489       server_scores.num_entries = i + 1;
10490     }
10491
10492     server_scores.entry[i] = score_entry;
10493   }
10494
10495   freeSetupFileHash(score_hash);
10496 }
10497
10498 void LoadServerScore(int nr, boolean download_score)
10499 {
10500   if (!setup.use_api_server)
10501     return;
10502
10503   // always start with reliable default values
10504   setServerScoreInfoToDefaults();
10505
10506   // 1st step: load server scores from cache file (which may not exist)
10507   // (this should prevent reading it while the thread is writing to it)
10508   LoadServerScoreFromCache(nr);
10509
10510   if (download_score && runtime.use_api_server)
10511   {
10512     // 2nd step: download server scores from score server to cache file
10513     // (as thread, as it might time out if the server is not reachable)
10514     ApiGetScoreAsThread(nr);
10515   }
10516 }
10517
10518 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10519 {
10520   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10521
10522   // if score tape not uploaded, ask for uploading missing tapes later
10523   if (!setup.has_remaining_tapes)
10524     setup.ask_for_remaining_tapes = TRUE;
10525
10526   setup.provide_uploading_tapes = TRUE;
10527   setup.has_remaining_tapes = TRUE;
10528
10529   SaveSetup_ServerSetup();
10530 }
10531
10532 void SaveServerScore(int nr, boolean tape_saved)
10533 {
10534   if (!runtime.use_api_server)
10535   {
10536     PrepareScoreTapesForUpload(leveldir_current->subdir);
10537
10538     return;
10539   }
10540
10541   ApiAddScoreAsThread(nr, tape_saved, NULL);
10542 }
10543
10544 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10545                              char *score_tape_filename)
10546 {
10547   if (!runtime.use_api_server)
10548     return;
10549
10550   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10551 }
10552
10553 void LoadLocalAndServerScore(int nr, boolean download_score)
10554 {
10555   int last_added_local = scores.last_added_local;
10556   boolean force_last_added = scores.force_last_added;
10557
10558   // needed if only showing server scores
10559   setScoreInfoToDefaults();
10560
10561   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10562     LoadScore(nr);
10563
10564   // restore last added local score entry (before merging server scores)
10565   scores.last_added = scores.last_added_local = last_added_local;
10566
10567   if (setup.use_api_server &&
10568       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10569   {
10570     // load server scores from cache file and trigger update from server
10571     LoadServerScore(nr, download_score);
10572
10573     // merge local scores with scores from server
10574     MergeServerScore();
10575   }
10576
10577   if (force_last_added)
10578     scores.force_last_added = force_last_added;
10579 }
10580
10581
10582 // ============================================================================
10583 // setup file functions
10584 // ============================================================================
10585
10586 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10587
10588
10589 static struct TokenInfo global_setup_tokens[] =
10590 {
10591   {
10592     TYPE_STRING,
10593     &setup.player_name,                         "player_name"
10594   },
10595   {
10596     TYPE_SWITCH,
10597     &setup.multiple_users,                      "multiple_users"
10598   },
10599   {
10600     TYPE_SWITCH,
10601     &setup.sound,                               "sound"
10602   },
10603   {
10604     TYPE_SWITCH,
10605     &setup.sound_loops,                         "repeating_sound_loops"
10606   },
10607   {
10608     TYPE_SWITCH,
10609     &setup.sound_music,                         "background_music"
10610   },
10611   {
10612     TYPE_SWITCH,
10613     &setup.sound_simple,                        "simple_sound_effects"
10614   },
10615   {
10616     TYPE_SWITCH,
10617     &setup.toons,                               "toons"
10618   },
10619   {
10620     TYPE_SWITCH,
10621     &setup.global_animations,                   "global_animations"
10622   },
10623   {
10624     TYPE_SWITCH,
10625     &setup.scroll_delay,                        "scroll_delay"
10626   },
10627   {
10628     TYPE_SWITCH,
10629     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10630   },
10631   {
10632     TYPE_INTEGER,
10633     &setup.scroll_delay_value,                  "scroll_delay_value"
10634   },
10635   {
10636     TYPE_STRING,
10637     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10638   },
10639   {
10640     TYPE_INTEGER,
10641     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10642   },
10643   {
10644     TYPE_SWITCH,
10645     &setup.fade_screens,                        "fade_screens"
10646   },
10647   {
10648     TYPE_SWITCH,
10649     &setup.autorecord,                          "automatic_tape_recording"
10650   },
10651   {
10652     TYPE_SWITCH,
10653     &setup.autorecord_after_replay,             "autorecord_after_replay"
10654   },
10655   {
10656     TYPE_SWITCH,
10657     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10658   },
10659   {
10660     TYPE_SWITCH,
10661     &setup.show_titlescreen,                    "show_titlescreen"
10662   },
10663   {
10664     TYPE_SWITCH,
10665     &setup.quick_doors,                         "quick_doors"
10666   },
10667   {
10668     TYPE_SWITCH,
10669     &setup.team_mode,                           "team_mode"
10670   },
10671   {
10672     TYPE_SWITCH,
10673     &setup.handicap,                            "handicap"
10674   },
10675   {
10676     TYPE_SWITCH,
10677     &setup.skip_levels,                         "skip_levels"
10678   },
10679   {
10680     TYPE_SWITCH_3_STATES,
10681     &setup.allow_skipping_levels,               "allow_skipping_levels"
10682   },
10683   {
10684     TYPE_SWITCH,
10685     &setup.increment_levels,                    "increment_levels"
10686   },
10687   {
10688     TYPE_SWITCH,
10689     &setup.auto_play_next_level,                "auto_play_next_level"
10690   },
10691   {
10692     TYPE_SWITCH,
10693     &setup.count_score_after_game,              "count_score_after_game"
10694   },
10695   {
10696     TYPE_SWITCH,
10697     &setup.show_scores_after_game,              "show_scores_after_game"
10698   },
10699   {
10700     TYPE_SWITCH,
10701     &setup.time_limit,                          "time_limit"
10702   },
10703   {
10704     TYPE_SWITCH,
10705     &setup.fullscreen,                          "fullscreen"
10706   },
10707   {
10708     TYPE_INTEGER,
10709     &setup.window_scaling_percent,              "window_scaling_percent"
10710   },
10711   {
10712     TYPE_STRING,
10713     &setup.window_scaling_quality,              "window_scaling_quality"
10714   },
10715   {
10716     TYPE_STRING,
10717     &setup.screen_rendering_mode,               "screen_rendering_mode"
10718   },
10719   {
10720     TYPE_STRING,
10721     &setup.vsync_mode,                          "vsync_mode"
10722   },
10723   {
10724     TYPE_SWITCH,
10725     &setup.ask_on_escape,                       "ask_on_escape"
10726   },
10727   {
10728     TYPE_SWITCH,
10729     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10730   },
10731   {
10732     TYPE_SWITCH,
10733     &setup.ask_on_game_over,                    "ask_on_game_over"
10734   },
10735   {
10736     TYPE_SWITCH,
10737     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10738   },
10739   {
10740     TYPE_SWITCH,
10741     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10742   },
10743   {
10744     TYPE_SWITCH,
10745     &setup.quick_switch,                        "quick_player_switch"
10746   },
10747   {
10748     TYPE_SWITCH,
10749     &setup.input_on_focus,                      "input_on_focus"
10750   },
10751   {
10752     TYPE_SWITCH,
10753     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10754   },
10755   {
10756     TYPE_SWITCH,
10757     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10758   },
10759   {
10760     TYPE_SWITCH,
10761     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10762   },
10763   {
10764     TYPE_SWITCH,
10765     &setup.game_speed_extended,                 "game_speed_extended"
10766   },
10767   {
10768     TYPE_INTEGER,
10769     &setup.game_frame_delay,                    "game_frame_delay"
10770   },
10771   {
10772     TYPE_INTEGER,
10773     &setup.default_game_engine_type,            "default_game_engine_type"
10774   },
10775   {
10776     TYPE_SWITCH,
10777     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10778   },
10779   {
10780     TYPE_SWITCH,
10781     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10782   },
10783   {
10784     TYPE_SWITCH,
10785     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10786   },
10787   {
10788     TYPE_SWITCH,
10789     &setup.bd_show_invisible_outbox,            "bd_show_invisible_outbox"
10790   },
10791   {
10792     TYPE_SWITCH_3_STATES,
10793     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10794   },
10795   {
10796     TYPE_SWITCH_3_STATES,
10797     &setup.bd_pushing_graphics,                 "bd_pushing_graphics"
10798   },
10799   {
10800     TYPE_SWITCH_3_STATES,
10801     &setup.bd_up_down_graphics,                 "bd_up_down_graphics"
10802   },
10803   {
10804     TYPE_SWITCH_3_STATES,
10805     &setup.bd_skip_falling_sounds,              "bd_skip_falling_sounds"
10806   },
10807   {
10808     TYPE_INTEGER,
10809     &setup.bd_palette_c64,                      "bd_palette_c64"
10810   },
10811   {
10812     TYPE_INTEGER,
10813     &setup.bd_palette_c64dtv,                   "bd_palette_c64dtv"
10814   },
10815   {
10816     TYPE_INTEGER,
10817     &setup.bd_palette_atari,                    "bd_palette_atari"
10818   },
10819   {
10820     TYPE_INTEGER,
10821     &setup.bd_default_color_type,               "bd_default_color_type"
10822   },
10823   {
10824     TYPE_SWITCH,
10825     &setup.bd_random_colors,                    "bd_random_colors"
10826   },
10827   {
10828     TYPE_SWITCH,
10829     &setup.sp_show_border_elements,             "sp_show_border_elements"
10830   },
10831   {
10832     TYPE_SWITCH,
10833     &setup.small_game_graphics,                 "small_game_graphics"
10834   },
10835   {
10836     TYPE_SWITCH,
10837     &setup.show_load_save_buttons,              "show_load_save_buttons"
10838   },
10839   {
10840     TYPE_SWITCH,
10841     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10842   },
10843   {
10844     TYPE_STRING,
10845     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10846   },
10847   {
10848     TYPE_STRING,
10849     &setup.graphics_set,                        "graphics_set"
10850   },
10851   {
10852     TYPE_STRING,
10853     &setup.sounds_set,                          "sounds_set"
10854   },
10855   {
10856     TYPE_STRING,
10857     &setup.music_set,                           "music_set"
10858   },
10859   {
10860     TYPE_SWITCH_3_STATES,
10861     &setup.override_level_graphics,             "override_level_graphics"
10862   },
10863   {
10864     TYPE_SWITCH_3_STATES,
10865     &setup.override_level_sounds,               "override_level_sounds"
10866   },
10867   {
10868     TYPE_SWITCH_3_STATES,
10869     &setup.override_level_music,                "override_level_music"
10870   },
10871   {
10872     TYPE_INTEGER,
10873     &setup.volume_simple,                       "volume_simple"
10874   },
10875   {
10876     TYPE_INTEGER,
10877     &setup.volume_loops,                        "volume_loops"
10878   },
10879   {
10880     TYPE_INTEGER,
10881     &setup.volume_music,                        "volume_music"
10882   },
10883   {
10884     TYPE_SWITCH,
10885     &setup.audio_sample_rate_44100,             "audio_sample_rate_44100"
10886   },
10887   {
10888     TYPE_SWITCH,
10889     &setup.network_mode,                        "network_mode"
10890   },
10891   {
10892     TYPE_PLAYER,
10893     &setup.network_player_nr,                   "network_player"
10894   },
10895   {
10896     TYPE_STRING,
10897     &setup.network_server_hostname,             "network_server_hostname"
10898   },
10899   {
10900     TYPE_STRING,
10901     &setup.touch.control_type,                  "touch.control_type"
10902   },
10903   {
10904     TYPE_INTEGER,
10905     &setup.touch.move_distance,                 "touch.move_distance"
10906   },
10907   {
10908     TYPE_INTEGER,
10909     &setup.touch.drop_distance,                 "touch.drop_distance"
10910   },
10911   {
10912     TYPE_INTEGER,
10913     &setup.touch.transparency,                  "touch.transparency"
10914   },
10915   {
10916     TYPE_INTEGER,
10917     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10918   },
10919   {
10920     TYPE_INTEGER,
10921     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10922   },
10923   {
10924     TYPE_INTEGER,
10925     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10926   },
10927   {
10928     TYPE_INTEGER,
10929     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10930   },
10931   {
10932     TYPE_INTEGER,
10933     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10934   },
10935   {
10936     TYPE_INTEGER,
10937     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10938   },
10939   {
10940     TYPE_SWITCH,
10941     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10942   },
10943 };
10944
10945 static struct TokenInfo auto_setup_tokens[] =
10946 {
10947   {
10948     TYPE_INTEGER,
10949     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10950   },
10951 };
10952
10953 static struct TokenInfo server_setup_tokens[] =
10954 {
10955   {
10956     TYPE_STRING,
10957     &setup.player_uuid,                         "player_uuid"
10958   },
10959   {
10960     TYPE_INTEGER,
10961     &setup.player_version,                      "player_version"
10962   },
10963   {
10964     TYPE_SWITCH,
10965     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10966   },
10967   {
10968     TYPE_STRING,
10969     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10970   },
10971   {
10972     TYPE_STRING,
10973     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10974   },
10975   {
10976     TYPE_SWITCH,
10977     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10978   },
10979   {
10980     TYPE_SWITCH,
10981     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10982   },
10983   {
10984     TYPE_SWITCH,
10985     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10986   },
10987   {
10988     TYPE_SWITCH,
10989     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10990   },
10991   {
10992     TYPE_SWITCH,
10993     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10994   },
10995 };
10996
10997 static struct TokenInfo editor_setup_tokens[] =
10998 {
10999   {
11000     TYPE_SWITCH,
11001     &setup.editor.el_classic,                   "editor.el_classic"
11002   },
11003   {
11004     TYPE_SWITCH,
11005     &setup.editor.el_custom,                    "editor.el_custom"
11006   },
11007   {
11008     TYPE_SWITCH,
11009     &setup.editor.el_user_defined,              "editor.el_user_defined"
11010   },
11011   {
11012     TYPE_SWITCH,
11013     &setup.editor.el_dynamic,                   "editor.el_dynamic"
11014   },
11015   {
11016     TYPE_SWITCH,
11017     &setup.editor.el_headlines,                 "editor.el_headlines"
11018   },
11019   {
11020     TYPE_SWITCH,
11021     &setup.editor.show_element_token,           "editor.show_element_token"
11022   },
11023   {
11024     TYPE_SWITCH,
11025     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
11026   },
11027 };
11028
11029 static struct TokenInfo editor_cascade_setup_tokens[] =
11030 {
11031   {
11032     TYPE_SWITCH,
11033     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
11034   },
11035   {
11036     TYPE_SWITCH,
11037     &setup.editor_cascade.el_bdx,               "editor.cascade.el_bdx"
11038   },
11039   {
11040     TYPE_SWITCH,
11041     &setup.editor_cascade.el_bdx_effects,       "editor.cascade.el_bdx_effects"
11042   },
11043   {
11044     TYPE_SWITCH,
11045     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
11046   },
11047   {
11048     TYPE_SWITCH,
11049     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
11050   },
11051   {
11052     TYPE_SWITCH,
11053     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
11054   },
11055   {
11056     TYPE_SWITCH,
11057     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
11058   },
11059   {
11060     TYPE_SWITCH,
11061     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
11062   },
11063   {
11064     TYPE_SWITCH,
11065     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
11066   },
11067   {
11068     TYPE_SWITCH,
11069     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
11070   },
11071   {
11072     TYPE_SWITCH,
11073     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
11074   },
11075   {
11076     TYPE_SWITCH,
11077     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
11078   },
11079   {
11080     TYPE_SWITCH,
11081     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
11082   },
11083   {
11084     TYPE_SWITCH,
11085     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
11086   },
11087   {
11088     TYPE_SWITCH,
11089     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
11090   },
11091   {
11092     TYPE_SWITCH,
11093     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
11094   },
11095   {
11096     TYPE_SWITCH,
11097     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
11098   },
11099   {
11100     TYPE_SWITCH,
11101     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
11102   },
11103   {
11104     TYPE_SWITCH,
11105     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
11106   },
11107   {
11108     TYPE_SWITCH,
11109     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
11110   },
11111 };
11112
11113 static struct TokenInfo shortcut_setup_tokens[] =
11114 {
11115   {
11116     TYPE_KEY_X11,
11117     &setup.shortcut.save_game,                  "shortcut.save_game"
11118   },
11119   {
11120     TYPE_KEY_X11,
11121     &setup.shortcut.load_game,                  "shortcut.load_game"
11122   },
11123   {
11124     TYPE_KEY_X11,
11125     &setup.shortcut.restart_game,               "shortcut.restart_game"
11126   },
11127   {
11128     TYPE_KEY_X11,
11129     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
11130   },
11131   {
11132     TYPE_KEY_X11,
11133     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
11134   },
11135   {
11136     TYPE_KEY_X11,
11137     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
11138   },
11139   {
11140     TYPE_KEY_X11,
11141     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
11142   },
11143   {
11144     TYPE_KEY_X11,
11145     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
11146   },
11147   {
11148     TYPE_KEY_X11,
11149     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
11150   },
11151   {
11152     TYPE_KEY_X11,
11153     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
11154   },
11155   {
11156     TYPE_KEY_X11,
11157     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11158   },
11159   {
11160     TYPE_KEY_X11,
11161     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11162   },
11163   {
11164     TYPE_KEY_X11,
11165     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11166   },
11167   {
11168     TYPE_KEY_X11,
11169     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11170   },
11171   {
11172     TYPE_KEY_X11,
11173     &setup.shortcut.tape_record,                "shortcut.tape_record"
11174   },
11175   {
11176     TYPE_KEY_X11,
11177     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11178   },
11179   {
11180     TYPE_KEY_X11,
11181     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11182   },
11183   {
11184     TYPE_KEY_X11,
11185     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11186   },
11187   {
11188     TYPE_KEY_X11,
11189     &setup.shortcut.sound_music,                "shortcut.sound_music"
11190   },
11191   {
11192     TYPE_KEY_X11,
11193     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11194   },
11195   {
11196     TYPE_KEY_X11,
11197     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11198   },
11199   {
11200     TYPE_KEY_X11,
11201     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11202   },
11203   {
11204     TYPE_KEY_X11,
11205     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11206   },
11207   {
11208     TYPE_KEY_X11,
11209     &setup.shortcut.speed_fast,                 "shortcut.speed_fast"
11210   },
11211   {
11212     TYPE_KEY_X11,
11213     &setup.shortcut.speed_slow,                 "shortcut.speed_slow"
11214   },
11215 };
11216
11217 static struct SetupInputInfo setup_input;
11218 static struct TokenInfo player_setup_tokens[] =
11219 {
11220   {
11221     TYPE_BOOLEAN,
11222     &setup_input.use_joystick,                  ".use_joystick"
11223   },
11224   {
11225     TYPE_STRING,
11226     &setup_input.joy.device_name,               ".joy.device_name"
11227   },
11228   {
11229     TYPE_INTEGER,
11230     &setup_input.joy.xleft,                     ".joy.xleft"
11231   },
11232   {
11233     TYPE_INTEGER,
11234     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11235   },
11236   {
11237     TYPE_INTEGER,
11238     &setup_input.joy.xright,                    ".joy.xright"
11239   },
11240   {
11241     TYPE_INTEGER,
11242     &setup_input.joy.yupper,                    ".joy.yupper"
11243   },
11244   {
11245     TYPE_INTEGER,
11246     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11247   },
11248   {
11249     TYPE_INTEGER,
11250     &setup_input.joy.ylower,                    ".joy.ylower"
11251   },
11252   {
11253     TYPE_INTEGER,
11254     &setup_input.joy.snap,                      ".joy.snap_field"
11255   },
11256   {
11257     TYPE_INTEGER,
11258     &setup_input.joy.drop,                      ".joy.place_bomb"
11259   },
11260   {
11261     TYPE_KEY_X11,
11262     &setup_input.key.left,                      ".key.move_left"
11263   },
11264   {
11265     TYPE_KEY_X11,
11266     &setup_input.key.right,                     ".key.move_right"
11267   },
11268   {
11269     TYPE_KEY_X11,
11270     &setup_input.key.up,                        ".key.move_up"
11271   },
11272   {
11273     TYPE_KEY_X11,
11274     &setup_input.key.down,                      ".key.move_down"
11275   },
11276   {
11277     TYPE_KEY_X11,
11278     &setup_input.key.snap,                      ".key.snap_field"
11279   },
11280   {
11281     TYPE_KEY_X11,
11282     &setup_input.key.drop,                      ".key.place_bomb"
11283   },
11284 };
11285
11286 static struct TokenInfo system_setup_tokens[] =
11287 {
11288   {
11289     TYPE_STRING,
11290     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11291   },
11292   {
11293     TYPE_STRING,
11294     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11295   },
11296   {
11297     TYPE_STRING,
11298     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11299   },
11300   {
11301     TYPE_INTEGER,
11302     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11303   },
11304 };
11305
11306 static struct TokenInfo internal_setup_tokens[] =
11307 {
11308   {
11309     TYPE_STRING,
11310     &setup.internal.program_title,              "program_title"
11311   },
11312   {
11313     TYPE_STRING,
11314     &setup.internal.program_version,            "program_version"
11315   },
11316   {
11317     TYPE_STRING,
11318     &setup.internal.program_author,             "program_author"
11319   },
11320   {
11321     TYPE_STRING,
11322     &setup.internal.program_email,              "program_email"
11323   },
11324   {
11325     TYPE_STRING,
11326     &setup.internal.program_website,            "program_website"
11327   },
11328   {
11329     TYPE_STRING,
11330     &setup.internal.program_copyright,          "program_copyright"
11331   },
11332   {
11333     TYPE_STRING,
11334     &setup.internal.program_company,            "program_company"
11335   },
11336   {
11337     TYPE_STRING,
11338     &setup.internal.program_icon_file,          "program_icon_file"
11339   },
11340   {
11341     TYPE_STRING,
11342     &setup.internal.default_graphics_set,       "default_graphics_set"
11343   },
11344   {
11345     TYPE_STRING,
11346     &setup.internal.default_sounds_set,         "default_sounds_set"
11347   },
11348   {
11349     TYPE_STRING,
11350     &setup.internal.default_music_set,          "default_music_set"
11351   },
11352   {
11353     TYPE_STRING,
11354     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11355   },
11356   {
11357     TYPE_STRING,
11358     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11359   },
11360   {
11361     TYPE_STRING,
11362     &setup.internal.fallback_music_file,        "fallback_music_file"
11363   },
11364   {
11365     TYPE_STRING,
11366     &setup.internal.default_level_series,       "default_level_series"
11367   },
11368   {
11369     TYPE_INTEGER,
11370     &setup.internal.default_window_width,       "default_window_width"
11371   },
11372   {
11373     TYPE_INTEGER,
11374     &setup.internal.default_window_height,      "default_window_height"
11375   },
11376   {
11377     TYPE_BOOLEAN,
11378     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11379   },
11380   {
11381     TYPE_BOOLEAN,
11382     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11383   },
11384   {
11385     TYPE_BOOLEAN,
11386     &setup.internal.create_user_levelset,       "create_user_levelset"
11387   },
11388   {
11389     TYPE_BOOLEAN,
11390     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11391   },
11392   {
11393     TYPE_BOOLEAN,
11394     &setup.internal.menu_game,                  "menu_game"
11395   },
11396   {
11397     TYPE_BOOLEAN,
11398     &setup.internal.menu_engines,               "menu_engines"
11399   },
11400   {
11401     TYPE_BOOLEAN,
11402     &setup.internal.menu_editor,                "menu_editor"
11403   },
11404   {
11405     TYPE_BOOLEAN,
11406     &setup.internal.menu_graphics,              "menu_graphics"
11407   },
11408   {
11409     TYPE_BOOLEAN,
11410     &setup.internal.menu_sound,                 "menu_sound"
11411   },
11412   {
11413     TYPE_BOOLEAN,
11414     &setup.internal.menu_artwork,               "menu_artwork"
11415   },
11416   {
11417     TYPE_BOOLEAN,
11418     &setup.internal.menu_input,                 "menu_input"
11419   },
11420   {
11421     TYPE_BOOLEAN,
11422     &setup.internal.menu_touch,                 "menu_touch"
11423   },
11424   {
11425     TYPE_BOOLEAN,
11426     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11427   },
11428   {
11429     TYPE_BOOLEAN,
11430     &setup.internal.menu_exit,                  "menu_exit"
11431   },
11432   {
11433     TYPE_BOOLEAN,
11434     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11435   },
11436   {
11437     TYPE_BOOLEAN,
11438     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11439   },
11440   {
11441     TYPE_BOOLEAN,
11442     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11443   },
11444   {
11445     TYPE_BOOLEAN,
11446     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11447   },
11448   {
11449     TYPE_BOOLEAN,
11450     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11451   },
11452   {
11453     TYPE_BOOLEAN,
11454     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11455   },
11456   {
11457     TYPE_BOOLEAN,
11458     &setup.internal.menu_shortcuts_speed,       "menu_shortcuts_speed"
11459   },
11460   {
11461     TYPE_BOOLEAN,
11462     &setup.internal.info_title,                 "info_title"
11463   },
11464   {
11465     TYPE_BOOLEAN,
11466     &setup.internal.info_elements,              "info_elements"
11467   },
11468   {
11469     TYPE_BOOLEAN,
11470     &setup.internal.info_music,                 "info_music"
11471   },
11472   {
11473     TYPE_BOOLEAN,
11474     &setup.internal.info_credits,               "info_credits"
11475   },
11476   {
11477     TYPE_BOOLEAN,
11478     &setup.internal.info_program,               "info_program"
11479   },
11480   {
11481     TYPE_BOOLEAN,
11482     &setup.internal.info_version,               "info_version"
11483   },
11484   {
11485     TYPE_BOOLEAN,
11486     &setup.internal.info_levelset,              "info_levelset"
11487   },
11488   {
11489     TYPE_BOOLEAN,
11490     &setup.internal.info_exit,                  "info_exit"
11491   },
11492 };
11493
11494 static struct TokenInfo debug_setup_tokens[] =
11495 {
11496   {
11497     TYPE_INTEGER,
11498     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11499   },
11500   {
11501     TYPE_INTEGER,
11502     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11503   },
11504   {
11505     TYPE_INTEGER,
11506     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11507   },
11508   {
11509     TYPE_INTEGER,
11510     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11511   },
11512   {
11513     TYPE_INTEGER,
11514     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11515   },
11516   {
11517     TYPE_INTEGER,
11518     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11519   },
11520   {
11521     TYPE_INTEGER,
11522     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11523   },
11524   {
11525     TYPE_INTEGER,
11526     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11527   },
11528   {
11529     TYPE_INTEGER,
11530     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11531   },
11532   {
11533     TYPE_INTEGER,
11534     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11535   },
11536   {
11537     TYPE_KEY_X11,
11538     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11539   },
11540   {
11541     TYPE_KEY_X11,
11542     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11543   },
11544   {
11545     TYPE_KEY_X11,
11546     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11547   },
11548   {
11549     TYPE_KEY_X11,
11550     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11551   },
11552   {
11553     TYPE_KEY_X11,
11554     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11555   },
11556   {
11557     TYPE_KEY_X11,
11558     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11559   },
11560   {
11561     TYPE_KEY_X11,
11562     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11563   },
11564   {
11565     TYPE_KEY_X11,
11566     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11567   },
11568   {
11569     TYPE_KEY_X11,
11570     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11571   },
11572   {
11573     TYPE_KEY_X11,
11574     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11575   },
11576   {
11577     TYPE_BOOLEAN,
11578     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11579   {
11580     TYPE_BOOLEAN,
11581     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11582   },
11583   {
11584     TYPE_BOOLEAN,
11585     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11586   },
11587   {
11588     TYPE_SWITCH_3_STATES,
11589     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11590   },
11591   {
11592     TYPE_INTEGER,
11593     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11594   },
11595 };
11596
11597 static struct TokenInfo options_setup_tokens[] =
11598 {
11599   {
11600     TYPE_BOOLEAN,
11601     &setup.options.verbose,                     "options.verbose"
11602   },
11603   {
11604     TYPE_BOOLEAN,
11605     &setup.options.debug,                       "options.debug"
11606   },
11607   {
11608     TYPE_STRING,
11609     &setup.options.debug_mode,                  "options.debug_mode"
11610   },
11611 };
11612
11613 static void setSetupInfoToDefaults(struct SetupInfo *si)
11614 {
11615   int i;
11616
11617   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11618
11619   si->multiple_users = TRUE;
11620
11621   si->sound = TRUE;
11622   si->sound_loops = TRUE;
11623   si->sound_music = TRUE;
11624   si->sound_simple = TRUE;
11625   si->toons = TRUE;
11626   si->global_animations = TRUE;
11627   si->scroll_delay = TRUE;
11628   si->forced_scroll_delay = FALSE;
11629   si->scroll_delay_value = STD_SCROLL_DELAY;
11630   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11631   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11632   si->fade_screens = TRUE;
11633   si->autorecord = TRUE;
11634   si->autorecord_after_replay = TRUE;
11635   si->auto_pause_on_start = FALSE;
11636   si->show_titlescreen = TRUE;
11637   si->quick_doors = FALSE;
11638   si->team_mode = FALSE;
11639   si->handicap = TRUE;
11640   si->skip_levels = TRUE;
11641   si->allow_skipping_levels = STATE_ASK;
11642   si->increment_levels = TRUE;
11643   si->auto_play_next_level = TRUE;
11644   si->count_score_after_game = TRUE;
11645   si->show_scores_after_game = TRUE;
11646   si->time_limit = TRUE;
11647   si->fullscreen = FALSE;
11648   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11649   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11650   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11651   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11652   si->ask_on_escape = TRUE;
11653   si->ask_on_escape_editor = TRUE;
11654   si->ask_on_game_over = TRUE;
11655   si->ask_on_quit_game = TRUE;
11656   si->ask_on_quit_program = TRUE;
11657   si->quick_switch = FALSE;
11658   si->input_on_focus = FALSE;
11659   si->prefer_aga_graphics = TRUE;
11660   si->prefer_lowpass_sounds = FALSE;
11661   si->prefer_extra_panel_items = TRUE;
11662   si->game_speed_extended = FALSE;
11663   si->game_frame_delay = GAME_FRAME_DELAY;
11664   si->default_game_engine_type  = GAME_ENGINE_TYPE_RND;
11665   si->bd_skip_uncovering = FALSE;
11666   si->bd_skip_hatching = FALSE;
11667   si->bd_scroll_delay = TRUE;
11668   si->bd_show_invisible_outbox = FALSE;
11669   si->bd_smooth_movements = STATE_AUTO;
11670   si->bd_pushing_graphics = STATE_TRUE;
11671   si->bd_up_down_graphics = STATE_TRUE;
11672   si->bd_skip_falling_sounds = STATE_AUTO;
11673   si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11674   si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11675   si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11676   si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11677   si->bd_random_colors = FALSE;
11678   si->sp_show_border_elements = FALSE;
11679   si->small_game_graphics = FALSE;
11680   si->show_load_save_buttons = FALSE;
11681   si->show_undo_redo_buttons = FALSE;
11682   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11683
11684   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11685   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11686   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11687
11688   si->override_level_graphics = STATE_FALSE;
11689   si->override_level_sounds = STATE_FALSE;
11690   si->override_level_music = STATE_FALSE;
11691
11692   si->volume_simple = 100;              // percent
11693   si->volume_loops = 100;               // percent
11694   si->volume_music = 100;               // percent
11695   si->audio_sample_rate_44100 = FALSE;
11696
11697   si->network_mode = FALSE;
11698   si->network_player_nr = 0;            // first player
11699   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11700
11701   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11702   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11703   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11704   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11705   si->touch.draw_outlined = TRUE;
11706   si->touch.draw_pressed = TRUE;
11707
11708   for (i = 0; i < 2; i++)
11709   {
11710     char *default_grid_button[6][2] =
11711     {
11712       { "      ", "  ^^  " },
11713       { "      ", "  ^^  " },
11714       { "      ", "<<  >>" },
11715       { "      ", "<<  >>" },
11716       { "111222", "  vv  " },
11717       { "111222", "  vv  " }
11718     };
11719     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11720     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11721     int min_xsize = MIN(6, grid_xsize);
11722     int min_ysize = MIN(6, grid_ysize);
11723     int startx = grid_xsize - min_xsize;
11724     int starty = grid_ysize - min_ysize;
11725     int x, y;
11726
11727     // virtual buttons grid can only be set to defaults if video is initialized
11728     // (this will be repeated if virtual buttons are not loaded from setup file)
11729     if (video.initialized)
11730     {
11731       si->touch.grid_xsize[i] = grid_xsize;
11732       si->touch.grid_ysize[i] = grid_ysize;
11733     }
11734     else
11735     {
11736       si->touch.grid_xsize[i] = -1;
11737       si->touch.grid_ysize[i] = -1;
11738     }
11739
11740     for (x = 0; x < MAX_GRID_XSIZE; x++)
11741       for (y = 0; y < MAX_GRID_YSIZE; y++)
11742         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11743
11744     for (x = 0; x < min_xsize; x++)
11745       for (y = 0; y < min_ysize; y++)
11746         si->touch.grid_button[i][x][starty + y] =
11747           default_grid_button[y][0][x];
11748
11749     for (x = 0; x < min_xsize; x++)
11750       for (y = 0; y < min_ysize; y++)
11751         si->touch.grid_button[i][startx + x][starty + y] =
11752           default_grid_button[y][1][x];
11753   }
11754
11755   si->touch.grid_initialized            = video.initialized;
11756
11757   si->touch.overlay_buttons             = FALSE;
11758
11759   si->editor.el_boulderdash             = TRUE;
11760   si->editor.el_boulderdash_native      = TRUE;
11761   si->editor.el_boulderdash_effects     = TRUE;
11762   si->editor.el_emerald_mine            = TRUE;
11763   si->editor.el_emerald_mine_club       = TRUE;
11764   si->editor.el_more                    = TRUE;
11765   si->editor.el_sokoban                 = TRUE;
11766   si->editor.el_supaplex                = TRUE;
11767   si->editor.el_diamond_caves           = TRUE;
11768   si->editor.el_dx_boulderdash          = TRUE;
11769
11770   si->editor.el_mirror_magic            = TRUE;
11771   si->editor.el_deflektor               = TRUE;
11772
11773   si->editor.el_chars                   = TRUE;
11774   si->editor.el_steel_chars             = TRUE;
11775
11776   si->editor.el_classic                 = TRUE;
11777   si->editor.el_custom                  = TRUE;
11778
11779   si->editor.el_user_defined            = FALSE;
11780   si->editor.el_dynamic                 = TRUE;
11781
11782   si->editor.el_headlines               = TRUE;
11783
11784   si->editor.show_element_token         = FALSE;
11785
11786   si->editor.show_read_only_warning     = TRUE;
11787
11788   si->editor.use_template_for_new_levels = TRUE;
11789
11790   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11791   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11792   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11793   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11794   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11795
11796   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11797   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11798   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11799   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11800   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11801
11802   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11803   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11804   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11805   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11806   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11807   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11808
11809   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11810   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11811   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11812
11813   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11814   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11815   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11816   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11817
11818   si->shortcut.speed_fast       = DEFAULT_KEY_SPEED_FAST;
11819   si->shortcut.speed_slow       = DEFAULT_KEY_SPEED_SLOW;
11820
11821   for (i = 0; i < MAX_PLAYERS; i++)
11822   {
11823     si->input[i].use_joystick = FALSE;
11824     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11825     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11826     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11827     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11828     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11829     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11830     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11831     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11832     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11833     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11834     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11835     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11836     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11837     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11838     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11839   }
11840
11841   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11842   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11843   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11844   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11845
11846   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11847   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11848   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11849   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11850   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11851   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11852   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11853
11854   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11855
11856   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11857   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11858   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11859
11860   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11861   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11862   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11863
11864   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11865   si->internal.choose_from_top_leveldir = FALSE;
11866   si->internal.show_scaling_in_title = TRUE;
11867   si->internal.create_user_levelset = TRUE;
11868   si->internal.info_screens_from_main = FALSE;
11869
11870   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11871   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11872
11873   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11874   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11875   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11876   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11877   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11878   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11879   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11880   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11881   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11882   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11883
11884   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11885   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11886   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11887   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11888   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11889   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11890   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11891   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11892   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11893   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11894
11895   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11896   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11897
11898   si->debug.show_frames_per_second = FALSE;
11899
11900   si->debug.xsn_mode = STATE_AUTO;
11901   si->debug.xsn_percent = 0;
11902
11903   si->options.verbose = FALSE;
11904   si->options.debug = FALSE;
11905   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11906
11907 #if defined(PLATFORM_ANDROID)
11908   si->fullscreen = TRUE;
11909   si->touch.overlay_buttons = TRUE;
11910 #endif
11911
11912   setHideSetupEntry(&setup.debug.xsn_mode);
11913 }
11914
11915 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11916 {
11917   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11918 }
11919
11920 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11921 {
11922   si->player_uuid = NULL;       // (will be set later)
11923   si->player_version = 1;       // (will be set later)
11924
11925   si->use_api_server = TRUE;
11926   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11927   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11928   si->ask_for_uploading_tapes = TRUE;
11929   si->ask_for_remaining_tapes = FALSE;
11930   si->provide_uploading_tapes = TRUE;
11931   si->ask_for_using_api_server = TRUE;
11932   si->has_remaining_tapes = FALSE;
11933 }
11934
11935 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11936 {
11937   si->editor_cascade.el_bd              = TRUE;
11938   si->editor_cascade.el_bdx             = TRUE;
11939   si->editor_cascade.el_bdx_effects     = FALSE;
11940   si->editor_cascade.el_em              = TRUE;
11941   si->editor_cascade.el_emc             = TRUE;
11942   si->editor_cascade.el_rnd             = TRUE;
11943   si->editor_cascade.el_sb              = TRUE;
11944   si->editor_cascade.el_sp              = TRUE;
11945   si->editor_cascade.el_dc              = TRUE;
11946   si->editor_cascade.el_dx              = TRUE;
11947
11948   si->editor_cascade.el_mm              = TRUE;
11949   si->editor_cascade.el_df              = TRUE;
11950
11951   si->editor_cascade.el_chars           = FALSE;
11952   si->editor_cascade.el_steel_chars     = FALSE;
11953   si->editor_cascade.el_ce              = FALSE;
11954   si->editor_cascade.el_ge              = FALSE;
11955   si->editor_cascade.el_es              = FALSE;
11956   si->editor_cascade.el_ref             = FALSE;
11957   si->editor_cascade.el_user            = FALSE;
11958   si->editor_cascade.el_dynamic         = FALSE;
11959 }
11960
11961 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11962
11963 static char *getHideSetupToken(void *setup_value)
11964 {
11965   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11966
11967   if (setup_value != NULL)
11968     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11969
11970   return hide_setup_token;
11971 }
11972
11973 void setHideSetupEntry(void *setup_value)
11974 {
11975   char *hide_setup_token = getHideSetupToken(setup_value);
11976
11977   if (hide_setup_hash == NULL)
11978     hide_setup_hash = newSetupFileHash();
11979
11980   if (setup_value != NULL)
11981     setHashEntry(hide_setup_hash, hide_setup_token, "");
11982 }
11983
11984 void removeHideSetupEntry(void *setup_value)
11985 {
11986   char *hide_setup_token = getHideSetupToken(setup_value);
11987
11988   if (setup_value != NULL)
11989     removeHashEntry(hide_setup_hash, hide_setup_token);
11990 }
11991
11992 boolean hideSetupEntry(void *setup_value)
11993 {
11994   char *hide_setup_token = getHideSetupToken(setup_value);
11995
11996   return (setup_value != NULL &&
11997           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11998 }
11999
12000 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
12001                                       struct TokenInfo *token_info,
12002                                       int token_nr, char *token_text)
12003 {
12004   char *token_hide_text = getStringCat2(token_text, ".hide");
12005   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
12006
12007   // set the value of this setup option in the setup option structure
12008   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12009
12010   // check if this setup option should be hidden in the setup menu
12011   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12012     setHideSetupEntry(token_info[token_nr].value);
12013
12014   free(token_hide_text);
12015 }
12016
12017 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12018                                       struct TokenInfo *token_info,
12019                                       int token_nr)
12020 {
12021   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12022                             token_info[token_nr].text);
12023 }
12024
12025 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12026 {
12027   int i, pnr;
12028
12029   if (!setup_file_hash)
12030     return;
12031
12032   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12033     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12034
12035   setup.touch.grid_initialized = TRUE;
12036   for (i = 0; i < 2; i++)
12037   {
12038     int grid_xsize = setup.touch.grid_xsize[i];
12039     int grid_ysize = setup.touch.grid_ysize[i];
12040     int x, y;
12041
12042     // if virtual buttons are not loaded from setup file, repeat initializing
12043     // virtual buttons grid with default values later when video is initialized
12044     if (grid_xsize == -1 ||
12045         grid_ysize == -1)
12046     {
12047       setup.touch.grid_initialized = FALSE;
12048
12049       continue;
12050     }
12051
12052     for (y = 0; y < grid_ysize; y++)
12053     {
12054       char token_string[MAX_LINE_LEN];
12055
12056       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12057
12058       char *value_string = getHashEntry(setup_file_hash, token_string);
12059
12060       if (value_string == NULL)
12061         continue;
12062
12063       for (x = 0; x < grid_xsize; x++)
12064       {
12065         char c = value_string[x];
12066
12067         setup.touch.grid_button[i][x][y] =
12068           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12069       }
12070     }
12071   }
12072
12073   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12074     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12075
12076   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12077     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12078
12079   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12080   {
12081     char prefix[30];
12082
12083     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12084
12085     setup_input = setup.input[pnr];
12086     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12087     {
12088       char full_token[100];
12089
12090       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12091       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12092                                 full_token);
12093     }
12094     setup.input[pnr] = setup_input;
12095   }
12096
12097   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12098     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12099
12100   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12101     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12102
12103   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12104     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12105
12106   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12107     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12108
12109   setHideRelatedSetupEntries();
12110 }
12111
12112 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12113 {
12114   int i;
12115
12116   if (!setup_file_hash)
12117     return;
12118
12119   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12120     setSetupInfo(auto_setup_tokens, i,
12121                  getHashEntry(setup_file_hash,
12122                               auto_setup_tokens[i].text));
12123 }
12124
12125 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12126 {
12127   int i;
12128
12129   if (!setup_file_hash)
12130     return;
12131
12132   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12133     setSetupInfo(server_setup_tokens, i,
12134                  getHashEntry(setup_file_hash,
12135                               server_setup_tokens[i].text));
12136 }
12137
12138 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12139 {
12140   int i;
12141
12142   if (!setup_file_hash)
12143     return;
12144
12145   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12146     setSetupInfo(editor_cascade_setup_tokens, i,
12147                  getHashEntry(setup_file_hash,
12148                               editor_cascade_setup_tokens[i].text));
12149 }
12150
12151 void LoadUserNames(void)
12152 {
12153   int last_user_nr = user.nr;
12154   int i;
12155
12156   if (global.user_names != NULL)
12157   {
12158     for (i = 0; i < MAX_PLAYER_NAMES; i++)
12159       checked_free(global.user_names[i]);
12160
12161     checked_free(global.user_names);
12162   }
12163
12164   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12165
12166   for (i = 0; i < MAX_PLAYER_NAMES; i++)
12167   {
12168     user.nr = i;
12169
12170     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12171
12172     if (setup_file_hash)
12173     {
12174       char *player_name = getHashEntry(setup_file_hash, "player_name");
12175
12176       global.user_names[i] = getFixedUserName(player_name);
12177
12178       freeSetupFileHash(setup_file_hash);
12179     }
12180
12181     if (global.user_names[i] == NULL)
12182       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12183   }
12184
12185   user.nr = last_user_nr;
12186 }
12187
12188 void LoadSetupFromFilename(char *filename)
12189 {
12190   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12191
12192   if (setup_file_hash)
12193   {
12194     decodeSetupFileHash_Default(setup_file_hash);
12195
12196     freeSetupFileHash(setup_file_hash);
12197   }
12198   else
12199   {
12200     Debug("setup", "using default setup values");
12201   }
12202 }
12203
12204 static void LoadSetup_SpecialPostProcessing(void)
12205 {
12206   char *player_name_new;
12207
12208   // needed to work around problems with fixed length strings
12209   player_name_new = getFixedUserName(setup.player_name);
12210   free(setup.player_name);
12211   setup.player_name = player_name_new;
12212
12213   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12214   if (setup.scroll_delay == FALSE)
12215   {
12216     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12217     setup.scroll_delay = TRUE;                  // now always "on"
12218   }
12219
12220   // make sure that scroll delay value stays inside valid range
12221   setup.scroll_delay_value =
12222     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12223 }
12224
12225 void LoadSetup_Default(void)
12226 {
12227   char *filename;
12228
12229   // always start with reliable default values
12230   setSetupInfoToDefaults(&setup);
12231
12232   // try to load setup values from default setup file
12233   filename = getDefaultSetupFilename();
12234
12235   if (fileExists(filename))
12236     LoadSetupFromFilename(filename);
12237
12238   // try to load setup values from platform setup file
12239   filename = getPlatformSetupFilename();
12240
12241   if (fileExists(filename))
12242     LoadSetupFromFilename(filename);
12243
12244   // try to load setup values from user setup file
12245   filename = getSetupFilename();
12246
12247   LoadSetupFromFilename(filename);
12248
12249   LoadSetup_SpecialPostProcessing();
12250 }
12251
12252 void LoadSetup_AutoSetup(void)
12253 {
12254   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12255   SetupFileHash *setup_file_hash = NULL;
12256
12257   // always start with reliable default values
12258   setSetupInfoToDefaults_AutoSetup(&setup);
12259
12260   setup_file_hash = loadSetupFileHash(filename);
12261
12262   if (setup_file_hash)
12263   {
12264     decodeSetupFileHash_AutoSetup(setup_file_hash);
12265
12266     freeSetupFileHash(setup_file_hash);
12267   }
12268
12269   free(filename);
12270 }
12271
12272 void LoadSetup_ServerSetup(void)
12273 {
12274   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12275   SetupFileHash *setup_file_hash = NULL;
12276
12277   // always start with reliable default values
12278   setSetupInfoToDefaults_ServerSetup(&setup);
12279
12280   setup_file_hash = loadSetupFileHash(filename);
12281
12282   if (setup_file_hash)
12283   {
12284     decodeSetupFileHash_ServerSetup(setup_file_hash);
12285
12286     freeSetupFileHash(setup_file_hash);
12287   }
12288
12289   free(filename);
12290
12291   if (setup.player_uuid == NULL)
12292   {
12293     // player UUID does not yet exist in setup file
12294     setup.player_uuid = getStringCopy(getUUID());
12295     setup.player_version = 2;
12296
12297     SaveSetup_ServerSetup();
12298   }
12299 }
12300
12301 void LoadSetup_EditorCascade(void)
12302 {
12303   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12304   SetupFileHash *setup_file_hash = NULL;
12305
12306   // always start with reliable default values
12307   setSetupInfoToDefaults_EditorCascade(&setup);
12308
12309   setup_file_hash = loadSetupFileHash(filename);
12310
12311   if (setup_file_hash)
12312   {
12313     decodeSetupFileHash_EditorCascade(setup_file_hash);
12314
12315     freeSetupFileHash(setup_file_hash);
12316   }
12317
12318   free(filename);
12319 }
12320
12321 void LoadSetup(void)
12322 {
12323   LoadSetup_Default();
12324   LoadSetup_AutoSetup();
12325   LoadSetup_ServerSetup();
12326   LoadSetup_EditorCascade();
12327 }
12328
12329 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12330                                            char *mapping_line)
12331 {
12332   char mapping_guid[MAX_LINE_LEN];
12333   char *mapping_start, *mapping_end;
12334
12335   // get GUID from game controller mapping line: copy complete line
12336   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12337   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12338
12339   // get GUID from game controller mapping line: cut after GUID part
12340   mapping_start = strchr(mapping_guid, ',');
12341   if (mapping_start != NULL)
12342     *mapping_start = '\0';
12343
12344   // cut newline from game controller mapping line
12345   mapping_end = strchr(mapping_line, '\n');
12346   if (mapping_end != NULL)
12347     *mapping_end = '\0';
12348
12349   // add mapping entry to game controller mappings hash
12350   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12351 }
12352
12353 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12354                                                  char *filename)
12355 {
12356   FILE *file;
12357
12358   if (!(file = fopen(filename, MODE_READ)))
12359   {
12360     Warn("cannot read game controller mappings file '%s'", filename);
12361
12362     return;
12363   }
12364
12365   while (!feof(file))
12366   {
12367     char line[MAX_LINE_LEN];
12368
12369     if (!fgets(line, MAX_LINE_LEN, file))
12370       break;
12371
12372     addGameControllerMappingToHash(mappings_hash, line);
12373   }
12374
12375   fclose(file);
12376 }
12377
12378 void SaveSetup_Default(void)
12379 {
12380   char *filename = getSetupFilename();
12381   FILE *file;
12382   int i, pnr;
12383
12384   InitUserDataDirectory();
12385
12386   if (!(file = fopen(filename, MODE_WRITE)))
12387   {
12388     Warn("cannot write setup file '%s'", filename);
12389
12390     return;
12391   }
12392
12393   fprintFileHeader(file, SETUP_FILENAME);
12394
12395   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12396   {
12397     // just to make things nicer :)
12398     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12399         global_setup_tokens[i].value == &setup.sound                    ||
12400         global_setup_tokens[i].value == &setup.graphics_set             ||
12401         global_setup_tokens[i].value == &setup.volume_simple            ||
12402         global_setup_tokens[i].value == &setup.network_mode             ||
12403         global_setup_tokens[i].value == &setup.touch.control_type       ||
12404         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12405         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12406       fprintf(file, "\n");
12407
12408     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12409   }
12410
12411   for (i = 0; i < 2; i++)
12412   {
12413     int grid_xsize = setup.touch.grid_xsize[i];
12414     int grid_ysize = setup.touch.grid_ysize[i];
12415     int x, y;
12416
12417     fprintf(file, "\n");
12418
12419     for (y = 0; y < grid_ysize; y++)
12420     {
12421       char token_string[MAX_LINE_LEN];
12422       char value_string[MAX_LINE_LEN];
12423
12424       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12425
12426       for (x = 0; x < grid_xsize; x++)
12427       {
12428         char c = setup.touch.grid_button[i][x][y];
12429
12430         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12431       }
12432
12433       value_string[grid_xsize] = '\0';
12434
12435       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12436     }
12437   }
12438
12439   fprintf(file, "\n");
12440   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12441     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12442
12443   fprintf(file, "\n");
12444   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12445     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12446
12447   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12448   {
12449     char prefix[30];
12450
12451     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12452     fprintf(file, "\n");
12453
12454     setup_input = setup.input[pnr];
12455     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12456       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12457   }
12458
12459   fprintf(file, "\n");
12460   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12461     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12462
12463   // (internal setup values not saved to user setup file)
12464
12465   fprintf(file, "\n");
12466   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12467     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12468         setup.debug.xsn_mode != STATE_AUTO)
12469       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12470
12471   fprintf(file, "\n");
12472   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12473     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12474
12475   fclose(file);
12476
12477   SetFilePermissions(filename, PERMS_PRIVATE);
12478 }
12479
12480 void SaveSetup_AutoSetup(void)
12481 {
12482   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12483   FILE *file;
12484   int i;
12485
12486   InitUserDataDirectory();
12487
12488   if (!(file = fopen(filename, MODE_WRITE)))
12489   {
12490     Warn("cannot write auto setup file '%s'", filename);
12491
12492     free(filename);
12493
12494     return;
12495   }
12496
12497   fprintFileHeader(file, AUTOSETUP_FILENAME);
12498
12499   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12500     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12501
12502   fclose(file);
12503
12504   SetFilePermissions(filename, PERMS_PRIVATE);
12505
12506   free(filename);
12507 }
12508
12509 void SaveSetup_ServerSetup(void)
12510 {
12511   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12512   FILE *file;
12513   int i;
12514
12515   InitUserDataDirectory();
12516
12517   if (!(file = fopen(filename, MODE_WRITE)))
12518   {
12519     Warn("cannot write server setup file '%s'", filename);
12520
12521     free(filename);
12522
12523     return;
12524   }
12525
12526   fprintFileHeader(file, SERVERSETUP_FILENAME);
12527
12528   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12529   {
12530     // just to make things nicer :)
12531     if (server_setup_tokens[i].value == &setup.use_api_server)
12532       fprintf(file, "\n");
12533
12534     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12535   }
12536
12537   fclose(file);
12538
12539   SetFilePermissions(filename, PERMS_PRIVATE);
12540
12541   free(filename);
12542 }
12543
12544 void SaveSetup_EditorCascade(void)
12545 {
12546   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12547   FILE *file;
12548   int i;
12549
12550   InitUserDataDirectory();
12551
12552   if (!(file = fopen(filename, MODE_WRITE)))
12553   {
12554     Warn("cannot write editor cascade state file '%s'", filename);
12555
12556     free(filename);
12557
12558     return;
12559   }
12560
12561   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12562
12563   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12564     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12565
12566   fclose(file);
12567
12568   SetFilePermissions(filename, PERMS_PRIVATE);
12569
12570   free(filename);
12571 }
12572
12573 void SaveSetup(void)
12574 {
12575   SaveSetup_Default();
12576   SaveSetup_AutoSetup();
12577   SaveSetup_ServerSetup();
12578   SaveSetup_EditorCascade();
12579 }
12580
12581 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12582                                                   char *filename)
12583 {
12584   FILE *file;
12585
12586   if (!(file = fopen(filename, MODE_WRITE)))
12587   {
12588     Warn("cannot write game controller mappings file '%s'", filename);
12589
12590     return;
12591   }
12592
12593   BEGIN_HASH_ITERATION(mappings_hash, itr)
12594   {
12595     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12596   }
12597   END_HASH_ITERATION(mappings_hash, itr)
12598
12599   fclose(file);
12600 }
12601
12602 void SaveSetup_AddGameControllerMapping(char *mapping)
12603 {
12604   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12605   SetupFileHash *mappings_hash = newSetupFileHash();
12606
12607   InitUserDataDirectory();
12608
12609   // load existing personal game controller mappings
12610   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12611
12612   // add new mapping to personal game controller mappings
12613   addGameControllerMappingToHash(mappings_hash, mapping);
12614
12615   // save updated personal game controller mappings
12616   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12617
12618   freeSetupFileHash(mappings_hash);
12619   free(filename);
12620 }
12621
12622 void LoadCustomElementDescriptions(void)
12623 {
12624   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12625   SetupFileHash *setup_file_hash;
12626   int i;
12627
12628   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12629   {
12630     if (element_info[i].custom_description != NULL)
12631     {
12632       free(element_info[i].custom_description);
12633       element_info[i].custom_description = NULL;
12634     }
12635   }
12636
12637   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12638     return;
12639
12640   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12641   {
12642     char *token = getStringCat2(element_info[i].token_name, ".name");
12643     char *value = getHashEntry(setup_file_hash, token);
12644
12645     if (value != NULL)
12646       element_info[i].custom_description = getStringCopy(value);
12647
12648     free(token);
12649   }
12650
12651   freeSetupFileHash(setup_file_hash);
12652 }
12653
12654 static int getElementFromToken(char *token)
12655 {
12656   char *value = getHashEntry(element_token_hash, token);
12657
12658   if (value != NULL)
12659     return atoi(value);
12660
12661   Warn("unknown element token '%s'", token);
12662
12663   return EL_UNDEFINED;
12664 }
12665
12666 void FreeGlobalAnimEventInfo(void)
12667 {
12668   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12669
12670   if (gaei->event_list == NULL)
12671     return;
12672
12673   int i;
12674
12675   for (i = 0; i < gaei->num_event_lists; i++)
12676   {
12677     checked_free(gaei->event_list[i]->event_value);
12678     checked_free(gaei->event_list[i]);
12679   }
12680
12681   checked_free(gaei->event_list);
12682
12683   gaei->event_list = NULL;
12684   gaei->num_event_lists = 0;
12685 }
12686
12687 static int AddGlobalAnimEventList(void)
12688 {
12689   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12690   int list_pos = gaei->num_event_lists++;
12691
12692   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12693                                      sizeof(struct GlobalAnimEventListInfo *));
12694
12695   gaei->event_list[list_pos] =
12696     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12697
12698   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12699
12700   gaeli->event_value = NULL;
12701   gaeli->num_event_values = 0;
12702
12703   return list_pos;
12704 }
12705
12706 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12707 {
12708   // do not add empty global animation events
12709   if (event_value == ANIM_EVENT_NONE)
12710     return list_pos;
12711
12712   // if list position is undefined, create new list
12713   if (list_pos == ANIM_EVENT_UNDEFINED)
12714     list_pos = AddGlobalAnimEventList();
12715
12716   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12717   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12718   int value_pos = gaeli->num_event_values++;
12719
12720   gaeli->event_value = checked_realloc(gaeli->event_value,
12721                                        gaeli->num_event_values * sizeof(int *));
12722
12723   gaeli->event_value[value_pos] = event_value;
12724
12725   return list_pos;
12726 }
12727
12728 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12729 {
12730   if (list_pos == ANIM_EVENT_UNDEFINED)
12731     return ANIM_EVENT_NONE;
12732
12733   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12734   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12735
12736   return gaeli->event_value[value_pos];
12737 }
12738
12739 int GetGlobalAnimEventValueCount(int list_pos)
12740 {
12741   if (list_pos == ANIM_EVENT_UNDEFINED)
12742     return 0;
12743
12744   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12745   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12746
12747   return gaeli->num_event_values;
12748 }
12749
12750 // This function checks if a string <s> of the format "string1, string2, ..."
12751 // exactly contains a string <s_contained>.
12752
12753 static boolean string_has_parameter(char *s, char *s_contained)
12754 {
12755   char *substring;
12756
12757   if (s == NULL || s_contained == NULL)
12758     return FALSE;
12759
12760   if (strlen(s_contained) > strlen(s))
12761     return FALSE;
12762
12763   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12764   {
12765     char next_char = s[strlen(s_contained)];
12766
12767     // check if next character is delimiter or whitespace
12768     if (next_char == ',' || next_char == '\0' ||
12769         next_char == ' ' || next_char == '\t')
12770       return TRUE;
12771   }
12772
12773   // check if string contains another parameter string after a comma
12774   substring = strchr(s, ',');
12775   if (substring == NULL)        // string does not contain a comma
12776     return FALSE;
12777
12778   // advance string pointer to next character after the comma
12779   substring++;
12780
12781   // skip potential whitespaces after the comma
12782   while (*substring == ' ' || *substring == '\t')
12783     substring++;
12784
12785   return string_has_parameter(substring, s_contained);
12786 }
12787
12788 static int get_anim_parameter_value_ce(char *s)
12789 {
12790   char *s_ptr = s;
12791   char *pattern_1 = "ce_change:custom_";
12792   char *pattern_2 = ".page_";
12793   int pattern_1_len = strlen(pattern_1);
12794   char *matching_char = strstr(s_ptr, pattern_1);
12795   int result = ANIM_EVENT_NONE;
12796
12797   if (matching_char == NULL)
12798     return ANIM_EVENT_NONE;
12799
12800   result = ANIM_EVENT_CE_CHANGE;
12801
12802   s_ptr = matching_char + pattern_1_len;
12803
12804   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12805   if (*s_ptr >= '0' && *s_ptr <= '9')
12806   {
12807     int gic_ce_nr = (*s_ptr++ - '0');
12808
12809     if (*s_ptr >= '0' && *s_ptr <= '9')
12810     {
12811       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12812
12813       if (*s_ptr >= '0' && *s_ptr <= '9')
12814         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12815     }
12816
12817     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12818       return ANIM_EVENT_NONE;
12819
12820     // custom element stored as 0 to 255
12821     gic_ce_nr--;
12822
12823     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12824   }
12825   else
12826   {
12827     // invalid custom element number specified
12828
12829     return ANIM_EVENT_NONE;
12830   }
12831
12832   // check for change page number ("page_X" or "page_XX") (optional)
12833   if (strPrefix(s_ptr, pattern_2))
12834   {
12835     s_ptr += strlen(pattern_2);
12836
12837     if (*s_ptr >= '0' && *s_ptr <= '9')
12838     {
12839       int gic_page_nr = (*s_ptr++ - '0');
12840
12841       if (*s_ptr >= '0' && *s_ptr <= '9')
12842         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12843
12844       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12845         return ANIM_EVENT_NONE;
12846
12847       // change page stored as 1 to 32 (0 means "all change pages")
12848
12849       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12850     }
12851     else
12852     {
12853       // invalid animation part number specified
12854
12855       return ANIM_EVENT_NONE;
12856     }
12857   }
12858
12859   // discard result if next character is neither delimiter nor whitespace
12860   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12861         *s_ptr == ' ' || *s_ptr == '\t'))
12862     return ANIM_EVENT_NONE;
12863
12864   return result;
12865 }
12866
12867 static int get_anim_parameter_value(char *s)
12868 {
12869   int event_value[] =
12870   {
12871     ANIM_EVENT_CLICK,
12872     ANIM_EVENT_INIT,
12873     ANIM_EVENT_START,
12874     ANIM_EVENT_END,
12875     ANIM_EVENT_POST
12876   };
12877   char *pattern_1[] =
12878   {
12879     "click:anim_",
12880     "init:anim_",
12881     "start:anim_",
12882     "end:anim_",
12883     "post:anim_"
12884   };
12885   char *pattern_2 = ".part_";
12886   char *matching_char = NULL;
12887   char *s_ptr = s;
12888   int pattern_1_len = 0;
12889   int result = ANIM_EVENT_NONE;
12890   int i;
12891
12892   result = get_anim_parameter_value_ce(s);
12893
12894   if (result != ANIM_EVENT_NONE)
12895     return result;
12896
12897   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12898   {
12899     matching_char = strstr(s_ptr, pattern_1[i]);
12900     pattern_1_len = strlen(pattern_1[i]);
12901     result = event_value[i];
12902
12903     if (matching_char != NULL)
12904       break;
12905   }
12906
12907   if (matching_char == NULL)
12908     return ANIM_EVENT_NONE;
12909
12910   s_ptr = matching_char + pattern_1_len;
12911
12912   // check for main animation number ("anim_X" or "anim_XX")
12913   if (*s_ptr >= '0' && *s_ptr <= '9')
12914   {
12915     int gic_anim_nr = (*s_ptr++ - '0');
12916
12917     if (*s_ptr >= '0' && *s_ptr <= '9')
12918       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12919
12920     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12921       return ANIM_EVENT_NONE;
12922
12923     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12924   }
12925   else
12926   {
12927     // invalid main animation number specified
12928
12929     return ANIM_EVENT_NONE;
12930   }
12931
12932   // check for animation part number ("part_X" or "part_XX") (optional)
12933   if (strPrefix(s_ptr, pattern_2))
12934   {
12935     s_ptr += strlen(pattern_2);
12936
12937     if (*s_ptr >= '0' && *s_ptr <= '9')
12938     {
12939       int gic_part_nr = (*s_ptr++ - '0');
12940
12941       if (*s_ptr >= '0' && *s_ptr <= '9')
12942         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12943
12944       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12945         return ANIM_EVENT_NONE;
12946
12947       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12948     }
12949     else
12950     {
12951       // invalid animation part number specified
12952
12953       return ANIM_EVENT_NONE;
12954     }
12955   }
12956
12957   // discard result if next character is neither delimiter nor whitespace
12958   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12959         *s_ptr == ' ' || *s_ptr == '\t'))
12960     return ANIM_EVENT_NONE;
12961
12962   return result;
12963 }
12964
12965 static int get_anim_parameter_values(char *s)
12966 {
12967   int list_pos = ANIM_EVENT_UNDEFINED;
12968   int event_value = ANIM_EVENT_DEFAULT;
12969
12970   if (string_has_parameter(s, "any"))
12971     event_value |= ANIM_EVENT_ANY;
12972
12973   if (string_has_parameter(s, "click:self") ||
12974       string_has_parameter(s, "click") ||
12975       string_has_parameter(s, "self"))
12976     event_value |= ANIM_EVENT_SELF;
12977
12978   if (string_has_parameter(s, "unclick:any"))
12979     event_value |= ANIM_EVENT_UNCLICK_ANY;
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   while (s != NULL)
12986   {
12987     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12988     event_value = get_anim_parameter_value(s);
12989
12990     // if animation event found, add it to global animation event list
12991     if (event_value != ANIM_EVENT_NONE)
12992       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12993
12994     // continue with next part of the string, starting with next comma
12995     s = strchr(s + 1, ',');
12996   }
12997
12998   return list_pos;
12999 }
13000
13001 static int get_anim_action_parameter_value(char *token)
13002 {
13003   // check most common default case first to massively speed things up
13004   if (strEqual(token, ARG_UNDEFINED))
13005     return ANIM_EVENT_ACTION_NONE;
13006
13007   int result = getImageIDFromToken(token);
13008
13009   if (result == -1)
13010   {
13011     char *gfx_token = getStringCat2("gfx.", token);
13012
13013     result = getImageIDFromToken(gfx_token);
13014
13015     checked_free(gfx_token);
13016   }
13017
13018   if (result == -1)
13019   {
13020     Key key = getKeyFromX11KeyName(token);
13021
13022     if (key != KSYM_UNDEFINED)
13023       result = -(int)key;
13024   }
13025
13026   if (result == -1)
13027   {
13028     if (isURL(token))
13029     {
13030       result = get_hash_from_string(token);     // unsigned int => int
13031       result = ABS(result);                     // may be negative now
13032       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13033
13034       setHashEntry(anim_url_hash, int2str(result, 0), token);
13035     }
13036   }
13037
13038   if (result == -1)
13039     result = ANIM_EVENT_ACTION_NONE;
13040
13041   return result;
13042 }
13043
13044 int get_parameter_value(char *value_raw, char *suffix, int type)
13045 {
13046   char *value = getStringToLower(value_raw);
13047   int result = 0;       // probably a save default value
13048
13049   if (strEqual(suffix, ".direction"))
13050   {
13051     result = (strEqual(value, "left")  ? MV_LEFT :
13052               strEqual(value, "right") ? MV_RIGHT :
13053               strEqual(value, "up")    ? MV_UP :
13054               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
13055   }
13056   else if (strEqual(suffix, ".position"))
13057   {
13058     result = (strEqual(value, "left")   ? POS_LEFT :
13059               strEqual(value, "right")  ? POS_RIGHT :
13060               strEqual(value, "top")    ? POS_TOP :
13061               strEqual(value, "upper")  ? POS_UPPER :
13062               strEqual(value, "middle") ? POS_MIDDLE :
13063               strEqual(value, "lower")  ? POS_LOWER :
13064               strEqual(value, "bottom") ? POS_BOTTOM :
13065               strEqual(value, "any")    ? POS_ANY :
13066               strEqual(value, "ce")     ? POS_CE :
13067               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13068               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
13069   }
13070   else if (strEqual(suffix, ".align"))
13071   {
13072     result = (strEqual(value, "left")   ? ALIGN_LEFT :
13073               strEqual(value, "right")  ? ALIGN_RIGHT :
13074               strEqual(value, "center") ? ALIGN_CENTER :
13075               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13076   }
13077   else if (strEqual(suffix, ".valign"))
13078   {
13079     result = (strEqual(value, "top")    ? VALIGN_TOP :
13080               strEqual(value, "bottom") ? VALIGN_BOTTOM :
13081               strEqual(value, "middle") ? VALIGN_MIDDLE :
13082               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13083   }
13084   else if (strEqual(suffix, ".anim_mode"))
13085   {
13086     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
13087               string_has_parameter(value, "loop")       ? ANIM_LOOP :
13088               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
13089               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
13090               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
13091               string_has_parameter(value, "random")     ? ANIM_RANDOM :
13092               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13093               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
13094               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
13095               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
13096               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13097               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
13098               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
13099               string_has_parameter(value, "all")        ? ANIM_ALL :
13100               string_has_parameter(value, "tiled")      ? ANIM_TILED :
13101               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
13102               ANIM_DEFAULT);
13103
13104     if (string_has_parameter(value, "once"))
13105       result |= ANIM_ONCE;
13106
13107     if (string_has_parameter(value, "reverse"))
13108       result |= ANIM_REVERSE;
13109
13110     if (string_has_parameter(value, "opaque_player"))
13111       result |= ANIM_OPAQUE_PLAYER;
13112
13113     if (string_has_parameter(value, "static_panel"))
13114       result |= ANIM_STATIC_PANEL;
13115   }
13116   else if (strEqual(suffix, ".init_event") ||
13117            strEqual(suffix, ".anim_event"))
13118   {
13119     result = get_anim_parameter_values(value);
13120   }
13121   else if (strEqual(suffix, ".init_delay_action") ||
13122            strEqual(suffix, ".anim_delay_action") ||
13123            strEqual(suffix, ".post_delay_action") ||
13124            strEqual(suffix, ".init_event_action") ||
13125            strEqual(suffix, ".anim_event_action"))
13126   {
13127     result = get_anim_action_parameter_value(value_raw);
13128   }
13129   else if (strEqual(suffix, ".class"))
13130   {
13131     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13132               get_hash_from_string(value));
13133   }
13134   else if (strEqual(suffix, ".style"))
13135   {
13136     result = STYLE_DEFAULT;
13137
13138     if (string_has_parameter(value, "accurate_borders"))
13139       result |= STYLE_ACCURATE_BORDERS;
13140
13141     if (string_has_parameter(value, "inner_corners"))
13142       result |= STYLE_INNER_CORNERS;
13143
13144     if (string_has_parameter(value, "reverse"))
13145       result |= STYLE_REVERSE;
13146
13147     if (string_has_parameter(value, "leftmost_position"))
13148       result |= STYLE_LEFTMOST_POSITION;
13149
13150     if (string_has_parameter(value, "block_clicks"))
13151       result |= STYLE_BLOCK;
13152
13153     if (string_has_parameter(value, "passthrough_clicks"))
13154       result |= STYLE_PASSTHROUGH;
13155
13156     if (string_has_parameter(value, "multiple_actions"))
13157       result |= STYLE_MULTIPLE_ACTIONS;
13158
13159     if (string_has_parameter(value, "consume_ce_event"))
13160       result |= STYLE_CONSUME_CE_EVENT;
13161   }
13162   else if (strEqual(suffix, ".fade_mode"))
13163   {
13164     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
13165               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
13166               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
13167               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
13168               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
13169               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
13170               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
13171               FADE_MODE_DEFAULT);
13172   }
13173   else if (strEqual(suffix, ".auto_delay_unit"))
13174   {
13175     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
13176               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13177               AUTO_DELAY_UNIT_DEFAULT);
13178   }
13179   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
13180   {
13181     result = gfx.get_font_from_token_function(value);
13182   }
13183   else          // generic parameter of type integer or boolean
13184   {
13185     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13186               type == TYPE_INTEGER ? get_integer_from_string(value) :
13187               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13188               ARG_UNDEFINED_VALUE);
13189   }
13190
13191   free(value);
13192
13193   return result;
13194 }
13195
13196 static int get_token_parameter_value(char *token, char *value_raw)
13197 {
13198   char *suffix;
13199
13200   if (token == NULL || value_raw == NULL)
13201     return ARG_UNDEFINED_VALUE;
13202
13203   suffix = strrchr(token, '.');
13204   if (suffix == NULL)
13205     suffix = token;
13206
13207   if (strEqual(suffix, ".element"))
13208     return getElementFromToken(value_raw);
13209
13210   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13211   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13212 }
13213
13214 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13215                                      boolean ignore_defaults)
13216 {
13217   int i;
13218
13219   for (i = 0; image_config_vars[i].token != NULL; i++)
13220   {
13221     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13222
13223     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13224     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13225       continue;
13226
13227     if (value != NULL)
13228       *image_config_vars[i].value =
13229         get_token_parameter_value(image_config_vars[i].token, value);
13230   }
13231 }
13232
13233 void InitMenuDesignSettings_Static(void)
13234 {
13235   // always start with reliable default values from static default config
13236   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13237 }
13238
13239 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13240 {
13241   int i;
13242
13243   // the following initializes hierarchical values from static configuration
13244
13245   // special case: initialize "ARG_DEFAULT" values in static default config
13246   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13247   titlescreen_initial_first_default.fade_mode  =
13248     title_initial_first_default.fade_mode;
13249   titlescreen_initial_first_default.fade_delay =
13250     title_initial_first_default.fade_delay;
13251   titlescreen_initial_first_default.post_delay =
13252     title_initial_first_default.post_delay;
13253   titlescreen_initial_first_default.auto_delay =
13254     title_initial_first_default.auto_delay;
13255   titlescreen_initial_first_default.auto_delay_unit =
13256     title_initial_first_default.auto_delay_unit;
13257   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13258   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13259   titlescreen_first_default.post_delay = title_first_default.post_delay;
13260   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13261   titlescreen_first_default.auto_delay_unit =
13262     title_first_default.auto_delay_unit;
13263   titlemessage_initial_first_default.fade_mode  =
13264     title_initial_first_default.fade_mode;
13265   titlemessage_initial_first_default.fade_delay =
13266     title_initial_first_default.fade_delay;
13267   titlemessage_initial_first_default.post_delay =
13268     title_initial_first_default.post_delay;
13269   titlemessage_initial_first_default.auto_delay =
13270     title_initial_first_default.auto_delay;
13271   titlemessage_initial_first_default.auto_delay_unit =
13272     title_initial_first_default.auto_delay_unit;
13273   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13274   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13275   titlemessage_first_default.post_delay = title_first_default.post_delay;
13276   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13277   titlemessage_first_default.auto_delay_unit =
13278     title_first_default.auto_delay_unit;
13279
13280   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13281   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13282   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13283   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13284   titlescreen_initial_default.auto_delay_unit =
13285     title_initial_default.auto_delay_unit;
13286   titlescreen_default.fade_mode  = title_default.fade_mode;
13287   titlescreen_default.fade_delay = title_default.fade_delay;
13288   titlescreen_default.post_delay = title_default.post_delay;
13289   titlescreen_default.auto_delay = title_default.auto_delay;
13290   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13291   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13292   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13293   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13294   titlemessage_initial_default.auto_delay_unit =
13295     title_initial_default.auto_delay_unit;
13296   titlemessage_default.fade_mode  = title_default.fade_mode;
13297   titlemessage_default.fade_delay = title_default.fade_delay;
13298   titlemessage_default.post_delay = title_default.post_delay;
13299   titlemessage_default.auto_delay = title_default.auto_delay;
13300   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13301
13302   // special case: initialize "ARG_DEFAULT" values in static default config
13303   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13304   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13305   {
13306     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13307     titlescreen_first[i] = titlescreen_first_default;
13308     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13309     titlemessage_first[i] = titlemessage_first_default;
13310
13311     titlescreen_initial[i] = titlescreen_initial_default;
13312     titlescreen[i] = titlescreen_default;
13313     titlemessage_initial[i] = titlemessage_initial_default;
13314     titlemessage[i] = titlemessage_default;
13315   }
13316
13317   // special case: initialize "ARG_DEFAULT" values in static default config
13318   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13319   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13320   {
13321     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13322       continue;
13323
13324     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13325     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13326     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13327   }
13328
13329   // special case: initialize "ARG_DEFAULT" values in static default config
13330   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13331   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13332   {
13333     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13334     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13335     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13336
13337     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13338       continue;
13339
13340     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13341   }
13342 }
13343
13344 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13345 {
13346   static struct
13347   {
13348     struct XY *dst, *src;
13349   }
13350   game_buttons_xy[] =
13351   {
13352     { &game.button.save,        &game.button.stop       },
13353     { &game.button.pause2,      &game.button.pause      },
13354     { &game.button.load,        &game.button.play       },
13355     { &game.button.undo,        &game.button.stop       },
13356     { &game.button.redo,        &game.button.play       },
13357
13358     { NULL,                     NULL                    }
13359   };
13360   int i, j;
13361
13362   // special case: initialize later added SETUP list size from LEVELS value
13363   if (menu.list_size[GAME_MODE_SETUP] == -1)
13364     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13365
13366   // set default position for snapshot buttons to stop/pause/play buttons
13367   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13368     if ((*game_buttons_xy[i].dst).x == -1 &&
13369         (*game_buttons_xy[i].dst).y == -1)
13370       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13371
13372   // --------------------------------------------------------------------------
13373   // dynamic viewports (including playfield margins, borders and alignments)
13374   // --------------------------------------------------------------------------
13375
13376   // dynamic viewports currently only supported for landscape mode
13377   int display_width  = MAX(video.display_width, video.display_height);
13378   int display_height = MIN(video.display_width, video.display_height);
13379
13380   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13381   {
13382     struct RectWithBorder *vp_window    = &viewport.window[i];
13383     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13384     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13385     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13386     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13387     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13388     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13389     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13390
13391     // adjust window size if min/max width/height is specified
13392
13393     if (vp_window->min_width != -1)
13394     {
13395       int window_width = display_width;
13396
13397       // when using static window height, use aspect ratio of display
13398       if (vp_window->min_height == -1)
13399         window_width = vp_window->height * display_width / display_height;
13400
13401       vp_window->width = MAX(vp_window->min_width, window_width);
13402     }
13403
13404     if (vp_window->min_height != -1)
13405     {
13406       int window_height = display_height;
13407
13408       // when using static window width, use aspect ratio of display
13409       if (vp_window->min_width == -1)
13410         window_height = vp_window->width * display_height / display_width;
13411
13412       vp_window->height = MAX(vp_window->min_height, window_height);
13413     }
13414
13415     if (vp_window->max_width != -1)
13416       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13417
13418     if (vp_window->max_height != -1)
13419       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13420
13421     int playfield_width  = vp_window->width;
13422     int playfield_height = vp_window->height;
13423
13424     // adjust playfield size and position according to specified margins
13425
13426     playfield_width  -= vp_playfield->margin_left;
13427     playfield_width  -= vp_playfield->margin_right;
13428
13429     playfield_height -= vp_playfield->margin_top;
13430     playfield_height -= vp_playfield->margin_bottom;
13431
13432     // adjust playfield size if min/max width/height is specified
13433
13434     if (vp_playfield->min_width != -1)
13435       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13436
13437     if (vp_playfield->min_height != -1)
13438       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13439
13440     if (vp_playfield->max_width != -1)
13441       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13442
13443     if (vp_playfield->max_height != -1)
13444       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13445
13446     // adjust playfield position according to specified alignment
13447
13448     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13449       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13450     else if (vp_playfield->align == ALIGN_CENTER)
13451       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13452     else if (vp_playfield->align == ALIGN_RIGHT)
13453       vp_playfield->x += playfield_width - vp_playfield->width;
13454
13455     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13456       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13457     else if (vp_playfield->valign == VALIGN_MIDDLE)
13458       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13459     else if (vp_playfield->valign == VALIGN_BOTTOM)
13460       vp_playfield->y += playfield_height - vp_playfield->height;
13461
13462     vp_playfield->x += vp_playfield->margin_left;
13463     vp_playfield->y += vp_playfield->margin_top;
13464
13465     // adjust individual playfield borders if only default border is specified
13466
13467     if (vp_playfield->border_left == -1)
13468       vp_playfield->border_left = vp_playfield->border_size;
13469     if (vp_playfield->border_right == -1)
13470       vp_playfield->border_right = vp_playfield->border_size;
13471     if (vp_playfield->border_top == -1)
13472       vp_playfield->border_top = vp_playfield->border_size;
13473     if (vp_playfield->border_bottom == -1)
13474       vp_playfield->border_bottom = vp_playfield->border_size;
13475
13476     // set dynamic playfield borders if borders are specified as undefined
13477     // (but only if window size was dynamic and playfield size was static)
13478
13479     if (dynamic_window_width && !dynamic_playfield_width)
13480     {
13481       if (vp_playfield->border_left == -1)
13482       {
13483         vp_playfield->border_left = (vp_playfield->x -
13484                                      vp_playfield->margin_left);
13485         vp_playfield->x     -= vp_playfield->border_left;
13486         vp_playfield->width += vp_playfield->border_left;
13487       }
13488
13489       if (vp_playfield->border_right == -1)
13490       {
13491         vp_playfield->border_right = (vp_window->width -
13492                                       vp_playfield->x -
13493                                       vp_playfield->width -
13494                                       vp_playfield->margin_right);
13495         vp_playfield->width += vp_playfield->border_right;
13496       }
13497     }
13498
13499     if (dynamic_window_height && !dynamic_playfield_height)
13500     {
13501       if (vp_playfield->border_top == -1)
13502       {
13503         vp_playfield->border_top = (vp_playfield->y -
13504                                     vp_playfield->margin_top);
13505         vp_playfield->y      -= vp_playfield->border_top;
13506         vp_playfield->height += vp_playfield->border_top;
13507       }
13508
13509       if (vp_playfield->border_bottom == -1)
13510       {
13511         vp_playfield->border_bottom = (vp_window->height -
13512                                        vp_playfield->y -
13513                                        vp_playfield->height -
13514                                        vp_playfield->margin_bottom);
13515         vp_playfield->height += vp_playfield->border_bottom;
13516       }
13517     }
13518
13519     // adjust playfield size to be a multiple of a defined alignment tile size
13520
13521     int align_size = vp_playfield->align_size;
13522     int playfield_xtiles = vp_playfield->width  / align_size;
13523     int playfield_ytiles = vp_playfield->height / align_size;
13524     int playfield_width_corrected  = playfield_xtiles * align_size;
13525     int playfield_height_corrected = playfield_ytiles * align_size;
13526     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13527                                  i == GFX_SPECIAL_ARG_EDITOR);
13528
13529     if (is_playfield_mode &&
13530         dynamic_playfield_width &&
13531         vp_playfield->width != playfield_width_corrected)
13532     {
13533       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13534
13535       vp_playfield->width = playfield_width_corrected;
13536
13537       if (vp_playfield->align == ALIGN_LEFT)
13538       {
13539         vp_playfield->border_left += playfield_xdiff;
13540       }
13541       else if (vp_playfield->align == ALIGN_RIGHT)
13542       {
13543         vp_playfield->border_right += playfield_xdiff;
13544       }
13545       else if (vp_playfield->align == ALIGN_CENTER)
13546       {
13547         int border_left_diff  = playfield_xdiff / 2;
13548         int border_right_diff = playfield_xdiff - border_left_diff;
13549
13550         vp_playfield->border_left  += border_left_diff;
13551         vp_playfield->border_right += border_right_diff;
13552       }
13553     }
13554
13555     if (is_playfield_mode &&
13556         dynamic_playfield_height &&
13557         vp_playfield->height != playfield_height_corrected)
13558     {
13559       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13560
13561       vp_playfield->height = playfield_height_corrected;
13562
13563       if (vp_playfield->valign == VALIGN_TOP)
13564       {
13565         vp_playfield->border_top += playfield_ydiff;
13566       }
13567       else if (vp_playfield->align == VALIGN_BOTTOM)
13568       {
13569         vp_playfield->border_right += playfield_ydiff;
13570       }
13571       else if (vp_playfield->align == VALIGN_MIDDLE)
13572       {
13573         int border_top_diff    = playfield_ydiff / 2;
13574         int border_bottom_diff = playfield_ydiff - border_top_diff;
13575
13576         vp_playfield->border_top    += border_top_diff;
13577         vp_playfield->border_bottom += border_bottom_diff;
13578       }
13579     }
13580
13581     // adjust door positions according to specified alignment
13582
13583     for (j = 0; j < 2; j++)
13584     {
13585       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13586
13587       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13588         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13589       else if (vp_door->align == ALIGN_CENTER)
13590         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13591       else if (vp_door->align == ALIGN_RIGHT)
13592         vp_door->x += vp_window->width - vp_door->width;
13593
13594       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13595         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13596       else if (vp_door->valign == VALIGN_MIDDLE)
13597         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13598       else if (vp_door->valign == VALIGN_BOTTOM)
13599         vp_door->y += vp_window->height - vp_door->height;
13600     }
13601   }
13602 }
13603
13604 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13605 {
13606   static struct
13607   {
13608     struct XYTileSize *dst, *src;
13609     int graphic;
13610   }
13611   editor_buttons_xy[] =
13612   {
13613     {
13614       &editor.button.element_left,      &editor.palette.element_left,
13615       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13616     },
13617     {
13618       &editor.button.element_middle,    &editor.palette.element_middle,
13619       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13620     },
13621     {
13622       &editor.button.element_right,     &editor.palette.element_right,
13623       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13624     },
13625
13626     { NULL,                     NULL                    }
13627   };
13628   int i;
13629
13630   // set default position for element buttons to element graphics
13631   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13632   {
13633     if ((*editor_buttons_xy[i].dst).x == -1 &&
13634         (*editor_buttons_xy[i].dst).y == -1)
13635     {
13636       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13637
13638       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13639
13640       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13641     }
13642   }
13643
13644   // adjust editor palette rows and columns if specified to be dynamic
13645
13646   if (editor.palette.cols == -1)
13647   {
13648     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13649     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13650     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13651
13652     editor.palette.cols = (vp_width - sc_width) / bt_width;
13653
13654     if (editor.palette.x == -1)
13655     {
13656       int palette_width = editor.palette.cols * bt_width + sc_width;
13657
13658       editor.palette.x = (vp_width - palette_width) / 2;
13659     }
13660   }
13661
13662   if (editor.palette.rows == -1)
13663   {
13664     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13665     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13666     int tx_height = getFontHeight(FONT_TEXT_2);
13667
13668     editor.palette.rows = (vp_height - tx_height) / bt_height;
13669
13670     if (editor.palette.y == -1)
13671     {
13672       int palette_height = editor.palette.rows * bt_height + tx_height;
13673
13674       editor.palette.y = (vp_height - palette_height) / 2;
13675     }
13676   }
13677 }
13678
13679 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13680                                                       boolean initialize)
13681 {
13682   // special case: check if network and preview player positions are redefined,
13683   // to compare this later against the main menu level preview being redefined
13684   struct TokenIntPtrInfo menu_config_players[] =
13685   {
13686     { "main.network_players.x", &menu.main.network_players.redefined    },
13687     { "main.network_players.y", &menu.main.network_players.redefined    },
13688     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13689     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13690     { "preview.x",              &preview.redefined                      },
13691     { "preview.y",              &preview.redefined                      }
13692   };
13693   int i;
13694
13695   if (initialize)
13696   {
13697     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13698       *menu_config_players[i].value = FALSE;
13699   }
13700   else
13701   {
13702     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13703       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13704         *menu_config_players[i].value = TRUE;
13705   }
13706 }
13707
13708 static void InitMenuDesignSettings_PreviewPlayers(void)
13709 {
13710   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13711 }
13712
13713 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13714 {
13715   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13716 }
13717
13718 static void LoadMenuDesignSettingsFromFilename(char *filename)
13719 {
13720   static struct TitleFadingInfo tfi;
13721   static struct TitleMessageInfo tmi;
13722   static struct TokenInfo title_tokens[] =
13723   {
13724     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13725     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13726     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13727     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13728     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13729
13730     { -1,               NULL,                   NULL                    }
13731   };
13732   static struct TokenInfo titlemessage_tokens[] =
13733   {
13734     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13735     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13736     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13737     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13738     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13739     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13740     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13741     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13742     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13743     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13744     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13745     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13746     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13747     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13748     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13749     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13750     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13751     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13752
13753     { -1,               NULL,                   NULL                    }
13754   };
13755   static struct
13756   {
13757     struct TitleFadingInfo *info;
13758     char *text;
13759   }
13760   title_info[] =
13761   {
13762     // initialize first titles from "enter screen" definitions, if defined
13763     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13764     { &title_first_default,             "menu.enter_screen.TITLE"       },
13765
13766     // initialize title screens from "next screen" definitions, if defined
13767     { &title_initial_default,           "menu.next_screen.TITLE"        },
13768     { &title_default,                   "menu.next_screen.TITLE"        },
13769
13770     { NULL,                             NULL                            }
13771   };
13772   static struct
13773   {
13774     struct TitleMessageInfo *array;
13775     char *text;
13776   }
13777   titlemessage_arrays[] =
13778   {
13779     // initialize first titles from "enter screen" definitions, if defined
13780     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13781     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13782     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13783     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13784
13785     // initialize titles from "next screen" definitions, if defined
13786     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13787     { titlescreen,                      "menu.next_screen.TITLE"        },
13788     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13789     { titlemessage,                     "menu.next_screen.TITLE"        },
13790
13791     // overwrite titles with title definitions, if defined
13792     { titlescreen_initial_first,        "[title_initial]"               },
13793     { titlescreen_first,                "[title]"                       },
13794     { titlemessage_initial_first,       "[title_initial]"               },
13795     { titlemessage_first,               "[title]"                       },
13796
13797     { titlescreen_initial,              "[title_initial]"               },
13798     { titlescreen,                      "[title]"                       },
13799     { titlemessage_initial,             "[title_initial]"               },
13800     { titlemessage,                     "[title]"                       },
13801
13802     // overwrite titles with title screen/message definitions, if defined
13803     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13804     { titlescreen_first,                "[titlescreen]"                 },
13805     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13806     { titlemessage_first,               "[titlemessage]"                },
13807
13808     { titlescreen_initial,              "[titlescreen_initial]"         },
13809     { titlescreen,                      "[titlescreen]"                 },
13810     { titlemessage_initial,             "[titlemessage_initial]"        },
13811     { titlemessage,                     "[titlemessage]"                },
13812
13813     { NULL,                             NULL                            }
13814   };
13815   SetupFileHash *setup_file_hash;
13816   int i, j, k;
13817
13818   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13819     return;
13820
13821   // the following initializes hierarchical values from dynamic configuration
13822
13823   // special case: initialize with default values that may be overwritten
13824   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13825   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13826   {
13827     struct TokenIntPtrInfo menu_config[] =
13828     {
13829       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13830       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13831       { "menu.list_size",       &menu.list_size[i]      }
13832     };
13833
13834     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13835     {
13836       char *token = menu_config[j].token;
13837       char *value = getHashEntry(setup_file_hash, token);
13838
13839       if (value != NULL)
13840         *menu_config[j].value = get_integer_from_string(value);
13841     }
13842   }
13843
13844   // special case: initialize with default values that may be overwritten
13845   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13846   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13847   {
13848     struct TokenIntPtrInfo menu_config[] =
13849     {
13850       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13851       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13852       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13853       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13854       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13855     };
13856
13857     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13858     {
13859       char *token = menu_config[j].token;
13860       char *value = getHashEntry(setup_file_hash, token);
13861
13862       if (value != NULL)
13863         *menu_config[j].value = get_integer_from_string(value);
13864     }
13865   }
13866
13867   // special case: initialize with default values that may be overwritten
13868   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13869   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13870   {
13871     struct TokenIntPtrInfo menu_config[] =
13872     {
13873       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13874       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13875     };
13876
13877     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13878     {
13879       char *token = menu_config[j].token;
13880       char *value = getHashEntry(setup_file_hash, token);
13881
13882       if (value != NULL)
13883         *menu_config[j].value = get_integer_from_string(value);
13884     }
13885   }
13886
13887   // special case: initialize with default values that may be overwritten
13888   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13889   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13890   {
13891     struct TokenIntPtrInfo menu_config[] =
13892     {
13893       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13894       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13895       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13896       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13897       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13898       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13899       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13900       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13901       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13902       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13903     };
13904
13905     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13906     {
13907       char *token = menu_config[j].token;
13908       char *value = getHashEntry(setup_file_hash, token);
13909
13910       if (value != NULL)
13911         *menu_config[j].value = get_integer_from_string(value);
13912     }
13913   }
13914
13915   // special case: initialize with default values that may be overwritten
13916   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13917   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13918   {
13919     struct TokenIntPtrInfo menu_config[] =
13920     {
13921       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13922       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13923       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13924       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13925       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13926       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13927       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13928       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13929       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13930     };
13931
13932     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13933     {
13934       char *token = menu_config[j].token;
13935       char *value = getHashEntry(setup_file_hash, token);
13936
13937       if (value != NULL)
13938         *menu_config[j].value = get_token_parameter_value(token, value);
13939     }
13940   }
13941
13942   // special case: initialize with default values that may be overwritten
13943   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13944   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13945   {
13946     struct
13947     {
13948       char *token_prefix;
13949       struct RectWithBorder *struct_ptr;
13950     }
13951     vp_struct[] =
13952     {
13953       { "viewport.window",      &viewport.window[i]     },
13954       { "viewport.playfield",   &viewport.playfield[i]  },
13955       { "viewport.door_1",      &viewport.door_1[i]     },
13956       { "viewport.door_2",      &viewport.door_2[i]     }
13957     };
13958
13959     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13960     {
13961       struct TokenIntPtrInfo vp_config[] =
13962       {
13963         { ".x",                 &vp_struct[j].struct_ptr->x             },
13964         { ".y",                 &vp_struct[j].struct_ptr->y             },
13965         { ".width",             &vp_struct[j].struct_ptr->width         },
13966         { ".height",            &vp_struct[j].struct_ptr->height        },
13967         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13968         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13969         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13970         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13971         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13972         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13973         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13974         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13975         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13976         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13977         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13978         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13979         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13980         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13981         { ".align",             &vp_struct[j].struct_ptr->align         },
13982         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13983       };
13984
13985       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13986       {
13987         char *token = getStringCat2(vp_struct[j].token_prefix,
13988                                     vp_config[k].token);
13989         char *value = getHashEntry(setup_file_hash, token);
13990
13991         if (value != NULL)
13992           *vp_config[k].value = get_token_parameter_value(token, value);
13993
13994         free(token);
13995       }
13996     }
13997   }
13998
13999   // special case: initialize with default values that may be overwritten
14000   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
14001   for (i = 0; title_info[i].info != NULL; i++)
14002   {
14003     struct TitleFadingInfo *info = title_info[i].info;
14004     char *base_token = title_info[i].text;
14005
14006     for (j = 0; title_tokens[j].type != -1; j++)
14007     {
14008       char *token = getStringCat2(base_token, title_tokens[j].text);
14009       char *value = getHashEntry(setup_file_hash, token);
14010
14011       if (value != NULL)
14012       {
14013         int parameter_value = get_token_parameter_value(token, value);
14014
14015         tfi = *info;
14016
14017         *(int *)title_tokens[j].value = (int)parameter_value;
14018
14019         *info = tfi;
14020       }
14021
14022       free(token);
14023     }
14024   }
14025
14026   // special case: initialize with default values that may be overwritten
14027   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14028   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14029   {
14030     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14031     char *base_token = titlemessage_arrays[i].text;
14032
14033     for (j = 0; titlemessage_tokens[j].type != -1; j++)
14034     {
14035       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14036       char *value = getHashEntry(setup_file_hash, token);
14037
14038       if (value != NULL)
14039       {
14040         int parameter_value = get_token_parameter_value(token, value);
14041
14042         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14043         {
14044           tmi = array[k];
14045
14046           if (titlemessage_tokens[j].type == TYPE_INTEGER)
14047             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
14048           else
14049             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14050
14051           array[k] = tmi;
14052         }
14053       }
14054
14055       free(token);
14056     }
14057   }
14058
14059   // read (and overwrite with) values that may be specified in config file
14060   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14061
14062   // special case: check if network and preview player positions are redefined
14063   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14064
14065   freeSetupFileHash(setup_file_hash);
14066 }
14067
14068 void LoadMenuDesignSettings(void)
14069 {
14070   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14071
14072   InitMenuDesignSettings_Static();
14073   InitMenuDesignSettings_SpecialPreProcessing();
14074   InitMenuDesignSettings_PreviewPlayers();
14075
14076   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14077   {
14078     // first look for special settings configured in level series config
14079     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14080
14081     if (fileExists(filename_base))
14082       LoadMenuDesignSettingsFromFilename(filename_base);
14083   }
14084
14085   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14086
14087   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14088     LoadMenuDesignSettingsFromFilename(filename_local);
14089
14090   InitMenuDesignSettings_SpecialPostProcessing();
14091 }
14092
14093 void LoadMenuDesignSettings_AfterGraphics(void)
14094 {
14095   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14096 }
14097
14098 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14099                                 boolean ignore_defaults)
14100 {
14101   int i;
14102
14103   for (i = 0; sound_config_vars[i].token != NULL; i++)
14104   {
14105     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14106
14107     // (ignore definitions set to "[DEFAULT]" which are already initialized)
14108     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14109       continue;
14110
14111     if (value != NULL)
14112       *sound_config_vars[i].value =
14113         get_token_parameter_value(sound_config_vars[i].token, value);
14114   }
14115 }
14116
14117 void InitSoundSettings_Static(void)
14118 {
14119   // always start with reliable default values from static default config
14120   InitSoundSettings_FromHash(sound_config_hash, FALSE);
14121 }
14122
14123 static void LoadSoundSettingsFromFilename(char *filename)
14124 {
14125   SetupFileHash *setup_file_hash;
14126
14127   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14128     return;
14129
14130   // read (and overwrite with) values that may be specified in config file
14131   InitSoundSettings_FromHash(setup_file_hash, TRUE);
14132
14133   freeSetupFileHash(setup_file_hash);
14134 }
14135
14136 void LoadSoundSettings(void)
14137 {
14138   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14139
14140   InitSoundSettings_Static();
14141
14142   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14143   {
14144     // first look for special settings configured in level series config
14145     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14146
14147     if (fileExists(filename_base))
14148       LoadSoundSettingsFromFilename(filename_base);
14149   }
14150
14151   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14152
14153   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14154     LoadSoundSettingsFromFilename(filename_local);
14155 }
14156
14157 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14158 {
14159   char *filename = getEditorSetupFilename();
14160   SetupFileList *setup_file_list, *list;
14161   SetupFileHash *element_hash;
14162   int num_unknown_tokens = 0;
14163   int i;
14164
14165   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14166     return;
14167
14168   element_hash = newSetupFileHash();
14169
14170   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14171     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14172
14173   // determined size may be larger than needed (due to unknown elements)
14174   *num_elements = 0;
14175   for (list = setup_file_list; list != NULL; list = list->next)
14176     (*num_elements)++;
14177
14178   // add space for up to 3 more elements for padding that may be needed
14179   *num_elements += 3;
14180
14181   // free memory for old list of elements, if needed
14182   checked_free(*elements);
14183
14184   // allocate memory for new list of elements
14185   *elements = checked_malloc(*num_elements * sizeof(int));
14186
14187   *num_elements = 0;
14188   for (list = setup_file_list; list != NULL; list = list->next)
14189   {
14190     char *value = getHashEntry(element_hash, list->token);
14191
14192     if (value == NULL)          // try to find obsolete token mapping
14193     {
14194       char *mapped_token = get_mapped_token(list->token);
14195
14196       if (mapped_token != NULL)
14197       {
14198         value = getHashEntry(element_hash, mapped_token);
14199
14200         free(mapped_token);
14201       }
14202     }
14203
14204     if (value != NULL)
14205     {
14206       (*elements)[(*num_elements)++] = atoi(value);
14207     }
14208     else
14209     {
14210       if (num_unknown_tokens == 0)
14211       {
14212         Warn("---");
14213         Warn("unknown token(s) found in config file:");
14214         Warn("- config file: '%s'", filename);
14215
14216         num_unknown_tokens++;
14217       }
14218
14219       Warn("- token: '%s'", list->token);
14220     }
14221   }
14222
14223   if (num_unknown_tokens > 0)
14224     Warn("---");
14225
14226   while (*num_elements % 4)     // pad with empty elements, if needed
14227     (*elements)[(*num_elements)++] = EL_EMPTY;
14228
14229   freeSetupFileList(setup_file_list);
14230   freeSetupFileHash(element_hash);
14231
14232 #if 0
14233   for (i = 0; i < *num_elements; i++)
14234     Debug("editor", "element '%s' [%d]\n",
14235           element_info[(*elements)[i]].token_name, (*elements)[i]);
14236 #endif
14237 }
14238
14239 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14240                                                      boolean is_sound)
14241 {
14242   SetupFileHash *setup_file_hash = NULL;
14243   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14244   char *filename_music, *filename_prefix, *filename_info;
14245   struct
14246   {
14247     char *token;
14248     char **value_ptr;
14249   }
14250   token_to_value_ptr[] =
14251   {
14252     { "title_header",   &tmp_music_file_info.title_header       },
14253     { "artist_header",  &tmp_music_file_info.artist_header      },
14254     { "album_header",   &tmp_music_file_info.album_header       },
14255     { "year_header",    &tmp_music_file_info.year_header        },
14256     { "played_header",  &tmp_music_file_info.played_header      },
14257
14258     { "title",          &tmp_music_file_info.title              },
14259     { "artist",         &tmp_music_file_info.artist             },
14260     { "album",          &tmp_music_file_info.album              },
14261     { "year",           &tmp_music_file_info.year               },
14262     { "played",         &tmp_music_file_info.played             },
14263
14264     { NULL,             NULL                                    },
14265   };
14266   int i;
14267
14268   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14269                     getCustomMusicFilename(basename));
14270
14271   if (filename_music == NULL)
14272     return NULL;
14273
14274   // ---------- try to replace file extension ----------
14275
14276   filename_prefix = getStringCopy(filename_music);
14277   if (strrchr(filename_prefix, '.') != NULL)
14278     *strrchr(filename_prefix, '.') = '\0';
14279   filename_info = getStringCat2(filename_prefix, ".txt");
14280
14281   if (fileExists(filename_info))
14282     setup_file_hash = loadSetupFileHash(filename_info);
14283
14284   free(filename_prefix);
14285   free(filename_info);
14286
14287   if (setup_file_hash == NULL)
14288   {
14289     // ---------- try to add file extension ----------
14290
14291     filename_prefix = getStringCopy(filename_music);
14292     filename_info = getStringCat2(filename_prefix, ".txt");
14293
14294     if (fileExists(filename_info))
14295       setup_file_hash = loadSetupFileHash(filename_info);
14296
14297     free(filename_prefix);
14298     free(filename_info);
14299   }
14300
14301   if (setup_file_hash == NULL)
14302     return NULL;
14303
14304   // ---------- music file info found ----------
14305
14306   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14307
14308   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14309   {
14310     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14311
14312     *token_to_value_ptr[i].value_ptr =
14313       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14314   }
14315
14316   tmp_music_file_info.basename = getStringCopy(basename);
14317   tmp_music_file_info.music = music;
14318   tmp_music_file_info.is_sound = is_sound;
14319
14320   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14321   *new_music_file_info = tmp_music_file_info;
14322
14323   return new_music_file_info;
14324 }
14325
14326 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14327 {
14328   return get_music_file_info_ext(basename, music, FALSE);
14329 }
14330
14331 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14332 {
14333   return get_music_file_info_ext(basename, sound, TRUE);
14334 }
14335
14336 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14337                                      char *basename, boolean is_sound)
14338 {
14339   for (; list != NULL; list = list->next)
14340     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14341       return TRUE;
14342
14343   return FALSE;
14344 }
14345
14346 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14347 {
14348   return music_info_listed_ext(list, basename, FALSE);
14349 }
14350
14351 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14352 {
14353   return music_info_listed_ext(list, basename, TRUE);
14354 }
14355
14356 void LoadMusicInfo(void)
14357 {
14358   int num_music_noconf = getMusicListSize_NoConf();
14359   int num_music = getMusicListSize();
14360   int num_sounds = getSoundListSize();
14361   struct FileInfo *music, *sound;
14362   struct MusicFileInfo *next, **new;
14363
14364   int i;
14365
14366   while (music_file_info != NULL)
14367   {
14368     next = music_file_info->next;
14369
14370     checked_free(music_file_info->basename);
14371
14372     checked_free(music_file_info->title_header);
14373     checked_free(music_file_info->artist_header);
14374     checked_free(music_file_info->album_header);
14375     checked_free(music_file_info->year_header);
14376     checked_free(music_file_info->played_header);
14377
14378     checked_free(music_file_info->title);
14379     checked_free(music_file_info->artist);
14380     checked_free(music_file_info->album);
14381     checked_free(music_file_info->year);
14382     checked_free(music_file_info->played);
14383
14384     free(music_file_info);
14385
14386     music_file_info = next;
14387   }
14388
14389   new = &music_file_info;
14390
14391   // get (configured or unconfigured) music file info for all levels
14392   for (i = leveldir_current->first_level;
14393        i <= leveldir_current->last_level; i++)
14394   {
14395     int music_nr;
14396
14397     if (levelset.music[i] != MUS_UNDEFINED)
14398     {
14399       // get music file info for configured level music
14400       music_nr = levelset.music[i];
14401     }
14402     else if (num_music_noconf > 0)
14403     {
14404       // get music file info for unconfigured level music
14405       int level_pos = i - leveldir_current->first_level;
14406
14407       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14408     }
14409     else
14410     {
14411       continue;
14412     }
14413
14414     char *basename = getMusicInfoEntryFilename(music_nr);
14415
14416     if (basename == NULL)
14417       continue;
14418
14419     if (!music_info_listed(music_file_info, basename))
14420     {
14421       *new = get_music_file_info(basename, music_nr);
14422
14423       if (*new != NULL)
14424         new = &(*new)->next;
14425     }
14426   }
14427
14428   // get music file info for all remaining configured music files
14429   for (i = 0; i < num_music; i++)
14430   {
14431     music = getMusicListEntry(i);
14432
14433     if (music->filename == NULL)
14434       continue;
14435
14436     if (strEqual(music->filename, UNDEFINED_FILENAME))
14437       continue;
14438
14439     // a configured file may be not recognized as music
14440     if (!FileIsMusic(music->filename))
14441       continue;
14442
14443     if (!music_info_listed(music_file_info, music->filename))
14444     {
14445       *new = get_music_file_info(music->filename, i);
14446
14447       if (*new != NULL)
14448         new = &(*new)->next;
14449     }
14450   }
14451
14452   // get sound file info for all configured sound files
14453   for (i = 0; i < num_sounds; i++)
14454   {
14455     sound = getSoundListEntry(i);
14456
14457     if (sound->filename == NULL)
14458       continue;
14459
14460     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14461       continue;
14462
14463     // a configured file may be not recognized as sound
14464     if (!FileIsSound(sound->filename))
14465       continue;
14466
14467     if (!sound_info_listed(music_file_info, sound->filename))
14468     {
14469       *new = get_sound_file_info(sound->filename, i);
14470       if (*new != NULL)
14471         new = &(*new)->next;
14472     }
14473   }
14474
14475   // add pointers to previous list nodes
14476
14477   struct MusicFileInfo *node = music_file_info;
14478
14479   while (node != NULL)
14480   {
14481     if (node->next)
14482       node->next->prev = node;
14483
14484     node = node->next;
14485   }
14486 }
14487
14488 static void add_helpanim_entry(int element, int action, int direction,
14489                                int delay, int *num_list_entries)
14490 {
14491   struct HelpAnimInfo *new_list_entry;
14492   (*num_list_entries)++;
14493
14494   helpanim_info =
14495     checked_realloc(helpanim_info,
14496                     *num_list_entries * sizeof(struct HelpAnimInfo));
14497   new_list_entry = &helpanim_info[*num_list_entries - 1];
14498
14499   new_list_entry->element = element;
14500   new_list_entry->action = action;
14501   new_list_entry->direction = direction;
14502   new_list_entry->delay = delay;
14503 }
14504
14505 static void print_unknown_token(char *filename, char *token, int token_nr)
14506 {
14507   if (token_nr == 0)
14508   {
14509     Warn("---");
14510     Warn("unknown token(s) found in config file:");
14511     Warn("- config file: '%s'", filename);
14512   }
14513
14514   Warn("- token: '%s'", token);
14515 }
14516
14517 static void print_unknown_token_end(int token_nr)
14518 {
14519   if (token_nr > 0)
14520     Warn("---");
14521 }
14522
14523 void LoadHelpAnimInfo(void)
14524 {
14525   char *filename = getHelpAnimFilename();
14526   SetupFileList *setup_file_list = NULL, *list;
14527   SetupFileHash *element_hash, *action_hash, *direction_hash;
14528   int num_list_entries = 0;
14529   int num_unknown_tokens = 0;
14530   int i;
14531
14532   if (fileExists(filename))
14533     setup_file_list = loadSetupFileList(filename);
14534
14535   if (setup_file_list == NULL)
14536   {
14537     // use reliable default values from static configuration
14538     SetupFileList *insert_ptr;
14539
14540     insert_ptr = setup_file_list =
14541       newSetupFileList(helpanim_config[0].token,
14542                        helpanim_config[0].value);
14543
14544     for (i = 1; helpanim_config[i].token; i++)
14545       insert_ptr = addListEntry(insert_ptr,
14546                                 helpanim_config[i].token,
14547                                 helpanim_config[i].value);
14548   }
14549
14550   element_hash   = newSetupFileHash();
14551   action_hash    = newSetupFileHash();
14552   direction_hash = newSetupFileHash();
14553
14554   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14555     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14556
14557   for (i = 0; i < NUM_ACTIONS; i++)
14558     setHashEntry(action_hash, element_action_info[i].suffix,
14559                  i_to_a(element_action_info[i].value));
14560
14561   // do not store direction index (bit) here, but direction value!
14562   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14563     setHashEntry(direction_hash, element_direction_info[i].suffix,
14564                  i_to_a(1 << element_direction_info[i].value));
14565
14566   for (list = setup_file_list; list != NULL; list = list->next)
14567   {
14568     char *element_token, *action_token, *direction_token;
14569     char *element_value, *action_value, *direction_value;
14570     int delay = atoi(list->value);
14571
14572     if (strEqual(list->token, "end"))
14573     {
14574       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14575
14576       continue;
14577     }
14578
14579     /* first try to break element into element/action/direction parts;
14580        if this does not work, also accept combined "element[.act][.dir]"
14581        elements (like "dynamite.active"), which are unique elements */
14582
14583     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14584     {
14585       element_value = getHashEntry(element_hash, list->token);
14586       if (element_value != NULL)        // element found
14587         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14588                            &num_list_entries);
14589       else
14590       {
14591         // no further suffixes found -- this is not an element
14592         print_unknown_token(filename, list->token, num_unknown_tokens++);
14593       }
14594
14595       continue;
14596     }
14597
14598     // token has format "<prefix>.<something>"
14599
14600     action_token = strchr(list->token, '.');    // suffix may be action ...
14601     direction_token = action_token;             // ... or direction
14602
14603     element_token = getStringCopy(list->token);
14604     *strchr(element_token, '.') = '\0';
14605
14606     element_value = getHashEntry(element_hash, element_token);
14607
14608     if (element_value == NULL)          // this is no element
14609     {
14610       element_value = getHashEntry(element_hash, list->token);
14611       if (element_value != NULL)        // combined element found
14612         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14613                            &num_list_entries);
14614       else
14615         print_unknown_token(filename, list->token, num_unknown_tokens++);
14616
14617       free(element_token);
14618
14619       continue;
14620     }
14621
14622     action_value = getHashEntry(action_hash, action_token);
14623
14624     if (action_value != NULL)           // action found
14625     {
14626       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14627                     &num_list_entries);
14628
14629       free(element_token);
14630
14631       continue;
14632     }
14633
14634     direction_value = getHashEntry(direction_hash, direction_token);
14635
14636     if (direction_value != NULL)        // direction found
14637     {
14638       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14639                          &num_list_entries);
14640
14641       free(element_token);
14642
14643       continue;
14644     }
14645
14646     if (strchr(action_token + 1, '.') == NULL)
14647     {
14648       // no further suffixes found -- this is not an action nor direction
14649
14650       element_value = getHashEntry(element_hash, list->token);
14651       if (element_value != NULL)        // combined element found
14652         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14653                            &num_list_entries);
14654       else
14655         print_unknown_token(filename, list->token, num_unknown_tokens++);
14656
14657       free(element_token);
14658
14659       continue;
14660     }
14661
14662     // token has format "<prefix>.<suffix>.<something>"
14663
14664     direction_token = strchr(action_token + 1, '.');
14665
14666     action_token = getStringCopy(action_token);
14667     *strchr(action_token + 1, '.') = '\0';
14668
14669     action_value = getHashEntry(action_hash, action_token);
14670
14671     if (action_value == NULL)           // this is no action
14672     {
14673       element_value = getHashEntry(element_hash, list->token);
14674       if (element_value != NULL)        // combined element found
14675         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14676                            &num_list_entries);
14677       else
14678         print_unknown_token(filename, list->token, num_unknown_tokens++);
14679
14680       free(element_token);
14681       free(action_token);
14682
14683       continue;
14684     }
14685
14686     direction_value = getHashEntry(direction_hash, direction_token);
14687
14688     if (direction_value != NULL)        // direction found
14689     {
14690       add_helpanim_entry(atoi(element_value), atoi(action_value),
14691                          atoi(direction_value), delay, &num_list_entries);
14692
14693       free(element_token);
14694       free(action_token);
14695
14696       continue;
14697     }
14698
14699     // this is no direction
14700
14701     element_value = getHashEntry(element_hash, list->token);
14702     if (element_value != NULL)          // combined element found
14703       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14704                          &num_list_entries);
14705     else
14706       print_unknown_token(filename, list->token, num_unknown_tokens++);
14707
14708     free(element_token);
14709     free(action_token);
14710   }
14711
14712   print_unknown_token_end(num_unknown_tokens);
14713
14714   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14715   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14716
14717   freeSetupFileList(setup_file_list);
14718   freeSetupFileHash(element_hash);
14719   freeSetupFileHash(action_hash);
14720   freeSetupFileHash(direction_hash);
14721
14722 #if 0
14723   for (i = 0; i < num_list_entries; i++)
14724     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14725           EL_NAME(helpanim_info[i].element),
14726           helpanim_info[i].element,
14727           helpanim_info[i].action,
14728           helpanim_info[i].direction,
14729           helpanim_info[i].delay);
14730 #endif
14731 }
14732
14733 void LoadHelpTextInfo(void)
14734 {
14735   char *filename = getHelpTextFilename();
14736   int i;
14737
14738   if (helptext_info != NULL)
14739   {
14740     freeSetupFileHash(helptext_info);
14741     helptext_info = NULL;
14742   }
14743
14744   if (fileExists(filename))
14745     helptext_info = loadSetupFileHash(filename);
14746
14747   if (helptext_info == NULL)
14748   {
14749     // use reliable default values from static configuration
14750     helptext_info = newSetupFileHash();
14751
14752     for (i = 0; helptext_config[i].token; i++)
14753       setHashEntry(helptext_info,
14754                    helptext_config[i].token,
14755                    helptext_config[i].value);
14756   }
14757
14758 #if 0
14759   BEGIN_HASH_ITERATION(helptext_info, itr)
14760   {
14761     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14762           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14763   }
14764   END_HASH_ITERATION(hash, itr)
14765 #endif
14766 }
14767
14768
14769 // ----------------------------------------------------------------------------
14770 // convert levels
14771 // ----------------------------------------------------------------------------
14772
14773 #define MAX_NUM_CONVERT_LEVELS          1000
14774
14775 void ConvertLevels(void)
14776 {
14777   static LevelDirTree *convert_leveldir = NULL;
14778   static int convert_level_nr = -1;
14779   static int num_levels_handled = 0;
14780   static int num_levels_converted = 0;
14781   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14782   int i;
14783
14784   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14785                                                global.convert_leveldir);
14786
14787   if (convert_leveldir == NULL)
14788     Fail("no such level identifier: '%s'", global.convert_leveldir);
14789
14790   leveldir_current = convert_leveldir;
14791
14792   if (global.convert_level_nr != -1)
14793   {
14794     convert_leveldir->first_level = global.convert_level_nr;
14795     convert_leveldir->last_level  = global.convert_level_nr;
14796   }
14797
14798   convert_level_nr = convert_leveldir->first_level;
14799
14800   PrintLine("=", 79);
14801   Print("Converting levels\n");
14802   PrintLine("-", 79);
14803   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14804   Print("Level series name:       '%s'\n", convert_leveldir->name);
14805   Print("Level series author:     '%s'\n", convert_leveldir->author);
14806   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14807   PrintLine("=", 79);
14808   Print("\n");
14809
14810   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14811     levels_failed[i] = FALSE;
14812
14813   while (convert_level_nr <= convert_leveldir->last_level)
14814   {
14815     char *level_filename;
14816     boolean new_level;
14817
14818     level_nr = convert_level_nr++;
14819
14820     Print("Level %03d: ", level_nr);
14821
14822     LoadLevel(level_nr);
14823     if (level.no_level_file || level.no_valid_file)
14824     {
14825       Print("(no level)\n");
14826       continue;
14827     }
14828
14829     Print("converting level ... ");
14830
14831 #if 0
14832     // special case: conversion of some EMC levels as requested by ACME
14833     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14834 #endif
14835
14836     level_filename = getDefaultLevelFilename(level_nr);
14837     new_level = !fileExists(level_filename);
14838
14839     if (new_level)
14840     {
14841       SaveLevel(level_nr);
14842
14843       num_levels_converted++;
14844
14845       Print("converted.\n");
14846     }
14847     else
14848     {
14849       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14850         levels_failed[level_nr] = TRUE;
14851
14852       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14853     }
14854
14855     num_levels_handled++;
14856   }
14857
14858   Print("\n");
14859   PrintLine("=", 79);
14860   Print("Number of levels handled: %d\n", num_levels_handled);
14861   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14862          (num_levels_handled ?
14863           num_levels_converted * 100 / num_levels_handled : 0));
14864   PrintLine("-", 79);
14865   Print("Summary (for automatic parsing by scripts):\n");
14866   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14867          convert_leveldir->identifier, num_levels_converted,
14868          num_levels_handled,
14869          (num_levels_handled ?
14870           num_levels_converted * 100 / num_levels_handled : 0));
14871
14872   if (num_levels_handled != num_levels_converted)
14873   {
14874     Print(", FAILED:");
14875     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14876       if (levels_failed[i])
14877         Print(" %03d", i);
14878   }
14879
14880   Print("\n");
14881   PrintLine("=", 79);
14882
14883   CloseAllAndExit(0);
14884 }
14885
14886
14887 // ----------------------------------------------------------------------------
14888 // create and save images for use in level sketches (raw BMP format)
14889 // ----------------------------------------------------------------------------
14890
14891 void CreateLevelSketchImages(void)
14892 {
14893   Bitmap *bitmap1;
14894   Bitmap *bitmap2;
14895   int i;
14896
14897   InitElementPropertiesGfxElement();
14898
14899   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14900   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14901
14902   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14903   {
14904     int element = getMappedElement(i);
14905     char basename1[16];
14906     char basename2[16];
14907     char *filename1;
14908     char *filename2;
14909
14910     sprintf(basename1, "%04d.bmp", i);
14911     sprintf(basename2, "%04ds.bmp", i);
14912
14913     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14914     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14915
14916     DrawSizedElement(0, 0, element, TILESIZE);
14917     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14918
14919     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14920       Fail("cannot save level sketch image file '%s'", filename1);
14921
14922     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14923     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14924
14925     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14926       Fail("cannot save level sketch image file '%s'", filename2);
14927
14928     free(filename1);
14929     free(filename2);
14930
14931     // create corresponding SQL statements (for normal and small images)
14932     if (i < 1000)
14933     {
14934       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14935       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14936     }
14937
14938     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14939     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14940
14941     // optional: create content for forum level sketch demonstration post
14942     if (options.debug)
14943       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14944   }
14945
14946   FreeBitmap(bitmap1);
14947   FreeBitmap(bitmap2);
14948
14949   if (options.debug)
14950     fprintf(stderr, "\n");
14951
14952   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14953
14954   CloseAllAndExit(0);
14955 }
14956
14957
14958 // ----------------------------------------------------------------------------
14959 // create and save images for element collecting animations (raw BMP format)
14960 // ----------------------------------------------------------------------------
14961
14962 static boolean createCollectImage(int element)
14963 {
14964   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14965 }
14966
14967 void CreateCollectElementImages(void)
14968 {
14969   int i, j;
14970   int num_steps = 8;
14971   int anim_frames = num_steps - 1;
14972   int tile_size = TILESIZE;
14973   int anim_width  = tile_size * anim_frames;
14974   int anim_height = tile_size;
14975   int num_collect_images = 0;
14976   int pos_collect_images = 0;
14977
14978   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14979     if (createCollectImage(i))
14980       num_collect_images++;
14981
14982   Info("Creating %d element collecting animation images ...",
14983        num_collect_images);
14984
14985   int dst_width  = anim_width * 2;
14986   int dst_height = anim_height * num_collect_images / 2;
14987   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14988   char *basename_bmp = "RocksCollect.bmp";
14989   char *basename_png = "RocksCollect.png";
14990   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14991   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14992   int len_filename_bmp = strlen(filename_bmp);
14993   int len_filename_png = strlen(filename_png);
14994   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14995   char cmd_convert[max_command_len];
14996
14997   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14998            filename_bmp,
14999            filename_png);
15000
15001   // force using RGBA surface for destination bitmap
15002   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
15003                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
15004
15005   dst_bitmap->surface =
15006     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15007
15008   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15009   {
15010     if (!createCollectImage(i))
15011       continue;
15012
15013     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15014     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15015     int graphic = el2img(i);
15016     char *token_name = element_info[i].token_name;
15017     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15018     Bitmap *src_bitmap;
15019     int src_x, src_y;
15020
15021     Info("- creating collecting image for '%s' ...", token_name);
15022
15023     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15024
15025     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15026                tile_size, tile_size, 0, 0);
15027
15028     // force using RGBA surface for temporary bitmap (using transparent black)
15029     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15030                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15031
15032     tmp_bitmap->surface =
15033       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15034
15035     tmp_bitmap->surface_masked = tmp_bitmap->surface;
15036
15037     for (j = 0; j < anim_frames; j++)
15038     {
15039       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15040       int frame_size = frame_size_final * num_steps;
15041       int offset = (tile_size - frame_size_final) / 2;
15042       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15043
15044       while (frame_size > frame_size_final)
15045       {
15046         frame_size /= 2;
15047
15048         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15049
15050         FreeBitmap(frame_bitmap);
15051
15052         frame_bitmap = half_bitmap;
15053       }
15054
15055       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15056                        frame_size_final, frame_size_final,
15057                        dst_x + j * tile_size + offset, dst_y + offset);
15058
15059       FreeBitmap(frame_bitmap);
15060     }
15061
15062     tmp_bitmap->surface_masked = NULL;
15063
15064     FreeBitmap(tmp_bitmap);
15065
15066     pos_collect_images++;
15067   }
15068
15069   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15070     Fail("cannot save element collecting image file '%s'", filename_bmp);
15071
15072   FreeBitmap(dst_bitmap);
15073
15074   Info("Converting image file from BMP to PNG ...");
15075
15076   if (system(cmd_convert) != 0)
15077     Fail("converting image file failed");
15078
15079   unlink(filename_bmp);
15080
15081   Info("Done.");
15082
15083   CloseAllAndExit(0);
15084 }
15085
15086
15087 // ----------------------------------------------------------------------------
15088 // create and save images for custom and group elements (raw BMP format)
15089 // ----------------------------------------------------------------------------
15090
15091 void CreateCustomElementImages(char *directory)
15092 {
15093   char *src_basename = "RocksCE-template.ilbm";
15094   char *dst_basename = "RocksCE.bmp";
15095   char *src_filename = getPath2(directory, src_basename);
15096   char *dst_filename = getPath2(directory, dst_basename);
15097   Bitmap *src_bitmap;
15098   Bitmap *bitmap;
15099   int yoffset_ce = 0;
15100   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15101   int i;
15102
15103   InitVideoDefaults();
15104
15105   ReCreateBitmap(&backbuffer, video.width, video.height);
15106
15107   src_bitmap = LoadImage(src_filename);
15108
15109   bitmap = CreateBitmap(TILEX * 16 * 2,
15110                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15111                         DEFAULT_DEPTH);
15112
15113   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15114   {
15115     int x = i % 16;
15116     int y = i / 16;
15117     int ii = i + 1;
15118     int j;
15119
15120     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15121                TILEX * x, TILEY * y + yoffset_ce);
15122
15123     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15124                TILEX, TILEY,
15125                TILEX * x + TILEX * 16,
15126                TILEY * y + yoffset_ce);
15127
15128     for (j = 2; j >= 0; j--)
15129     {
15130       int c = ii % 10;
15131
15132       BlitBitmap(src_bitmap, bitmap,
15133                  TILEX + c * 7, 0, 6, 10,
15134                  TILEX * x + 6 + j * 7,
15135                  TILEY * y + 11 + yoffset_ce);
15136
15137       BlitBitmap(src_bitmap, bitmap,
15138                  TILEX + c * 8, TILEY, 6, 10,
15139                  TILEX * 16 + TILEX * x + 6 + j * 8,
15140                  TILEY * y + 10 + yoffset_ce);
15141
15142       ii /= 10;
15143     }
15144   }
15145
15146   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15147   {
15148     int x = i % 16;
15149     int y = i / 16;
15150     int ii = i + 1;
15151     int j;
15152
15153     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15154                TILEX * x, TILEY * y + yoffset_ge);
15155
15156     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15157                TILEX, TILEY,
15158                TILEX * x + TILEX * 16,
15159                TILEY * y + yoffset_ge);
15160
15161     for (j = 1; j >= 0; j--)
15162     {
15163       int c = ii % 10;
15164
15165       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15166                  TILEX * x + 6 + j * 10,
15167                  TILEY * y + 11 + yoffset_ge);
15168
15169       BlitBitmap(src_bitmap, bitmap,
15170                  TILEX + c * 8, TILEY + 12, 6, 10,
15171                  TILEX * 16 + TILEX * x + 10 + j * 8,
15172                  TILEY * y + 10 + yoffset_ge);
15173
15174       ii /= 10;
15175     }
15176   }
15177
15178   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15179     Fail("cannot save CE graphics file '%s'", dst_filename);
15180
15181   FreeBitmap(bitmap);
15182
15183   CloseAllAndExit(0);
15184 }