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