added support for rocket launcher in 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 "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_INTEGER,                       CONF_VALUE_8_BIT(23),
311     &li.bd_cave_random_seed_c64,        0
312   },
313
314   {
315     -1,                                 -1,
316     -1,                                 -1,
317     NULL,                               -1
318   }
319 };
320
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
322 {
323   // (these values are the same for each player)
324   {
325     EL_PLAYER_1,                        -1,
326     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
327     &li.block_last_field,               FALSE   // default case for EM levels
328   },
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
332     &li.sp_block_last_field,            TRUE    // default case for SP levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
337     &li.instant_relocation,             FALSE
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
342     &li.can_pass_to_walkable,           FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
347     &li.block_snap_field,               TRUE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
352     &li.continuous_snapping,            TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
357     &li.shifted_relocation,             FALSE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
362     &li.lazy_relocation,                FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
367     &li.finish_dig_collect,             TRUE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
372     &li.keep_walkable_ce,               FALSE
373   },
374
375   // (these values are different for each player)
376   {
377     EL_PLAYER_1,                        -1,
378     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
379     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
380   },
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
384     &li.initial_player_gravity[0],      FALSE
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
389     &li.use_start_element[0],           FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
394     &li.start_element[0],               EL_PLAYER_1
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
399     &li.use_artwork_element[0],         FALSE
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
404     &li.artwork_element[0],             EL_PLAYER_1
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
409     &li.use_explosion_element[0],       FALSE
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
414     &li.explosion_element[0],           EL_PLAYER_1
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
419     &li.use_initial_inventory[0],       FALSE
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
424     &li.initial_inventory_size[0],      1
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
429     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
431   },
432
433   {
434     EL_PLAYER_2,                        -1,
435     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
436     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
437   },
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
441     &li.initial_player_gravity[1],      FALSE
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
446     &li.use_start_element[1],           FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
451     &li.start_element[1],               EL_PLAYER_2
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
456     &li.use_artwork_element[1],         FALSE
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
461     &li.artwork_element[1],             EL_PLAYER_2
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
466     &li.use_explosion_element[1],       FALSE
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
471     &li.explosion_element[1],           EL_PLAYER_2
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
476     &li.use_initial_inventory[1],       FALSE
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
481     &li.initial_inventory_size[1],      1
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
486     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
488   },
489
490   {
491     EL_PLAYER_3,                        -1,
492     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
493     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
494   },
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
498     &li.initial_player_gravity[2],      FALSE
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
503     &li.use_start_element[2],           FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
508     &li.start_element[2],               EL_PLAYER_3
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
513     &li.use_artwork_element[2],         FALSE
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
518     &li.artwork_element[2],             EL_PLAYER_3
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
523     &li.use_explosion_element[2],       FALSE
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
528     &li.explosion_element[2],           EL_PLAYER_3
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
533     &li.use_initial_inventory[2],       FALSE
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
538     &li.initial_inventory_size[2],      1
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
543     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
545   },
546
547   {
548     EL_PLAYER_4,                        -1,
549     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
550     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
551   },
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
555     &li.initial_player_gravity[3],      FALSE
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
560     &li.use_start_element[3],           FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
565     &li.start_element[3],               EL_PLAYER_4
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
570     &li.use_artwork_element[3],         FALSE
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
575     &li.artwork_element[3],             EL_PLAYER_4
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
580     &li.use_explosion_element[3],       FALSE
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
585     &li.explosion_element[3],           EL_PLAYER_4
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
590     &li.use_initial_inventory[3],       FALSE
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
595     &li.initial_inventory_size[3],      1
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
600     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
602   },
603
604   // (these values are only valid for BD style levels)
605   // (some values for BD style amoeba following below)
606   {
607     EL_BD_PLAYER,                       -1,
608     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
609     &li.bd_diagonal_movements,          FALSE
610   },
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
614     &li.bd_topmost_player_active,       TRUE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
619     &li.bd_pushing_prob,                25
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
624     &li.bd_pushing_prob_with_sweet,     100
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
629     &li.bd_push_mega_rock_with_sweet,   FALSE
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
634     &li.bd_snap_element,                EL_EMPTY
635   },
636
637   {
638     EL_BD_SAND,                         -1,
639     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
640     &li.bd_sand_looks_like,             EL_BD_SAND
641   },
642
643   {
644     EL_BD_ROCK,                         -1,
645     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
646     &li.bd_rock_turns_to_on_falling,    EL_BD_ROCK_FALLING
647   },
648   {
649     EL_BD_ROCK,                         -1,
650     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
651     &li.bd_rock_turns_to_on_impact,     EL_BD_ROCK
652   },
653
654   {
655     EL_BD_DIAMOND,                      -1,
656     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
657     &li.score[SC_DIAMOND_EXTRA],        20
658   },
659   {
660     EL_BD_DIAMOND,                      -1,
661     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
662     &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
663   },
664   {
665     EL_BD_DIAMOND,                      -1,
666     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
667     &li.bd_diamond_turns_to_on_impact,  EL_BD_DIAMOND
668   },
669
670   {
671     EL_BD_FIREFLY,                      -1,
672     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
673     &li.bd_firefly_explodes_to,         EL_BD_EXPLODING_1
674   },
675
676   {
677     EL_BD_FIREFLY_2,                    -1,
678     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
679     &li.bd_firefly_2_explodes_to,       EL_BD_EXPLODING_1
680   },
681
682   {
683     EL_BD_BUTTERFLY,                    -1,
684     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
685     &li.bd_butterfly_explodes_to,       EL_BD_DIAMOND_GROWING_1
686   },
687
688   {
689     EL_BD_BUTTERFLY_2,                  -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
691     &li.bd_butterfly_2_explodes_to,     EL_BD_DIAMOND_GROWING_1
692   },
693
694   {
695     EL_BD_STONEFLY,                     -1,
696     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
697     &li.bd_stonefly_explodes_to,        EL_BD_ROCK_GROWING_1
698   },
699
700   {
701     EL_BD_DRAGONFLY,                    -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
703     &li.bd_dragonfly_explodes_to,       EL_BD_EXPLODING_1
704   },
705
706   {
707     EL_BD_DIAMOND_GROWING_5,            -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_diamond_birth_turns_to,      EL_BD_DIAMOND
710   },
711
712   {
713     EL_BD_BOMB_EXPLODING_4,             -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_bomb_explosion_turns_to,     EL_BD_WALL
716   },
717
718   {
719     EL_BD_NITRO_PACK_EXPLODING_4,       -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_nitro_explosion_turns_to,    EL_EMPTY
722   },
723
724   {
725     EL_BD_EXPLODING_5,                  -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_explosion_turns_to,          EL_EMPTY
728   },
729
730   {
731     EL_BD_MAGIC_WALL,                   -1,
732     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
733     &li.bd_magic_wall_wait_hatching,    FALSE
734   },
735   {
736     EL_BD_MAGIC_WALL,                   -1,
737     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
738     &li.bd_magic_wall_stops_amoeba,     TRUE
739   },
740   {
741     EL_BD_MAGIC_WALL,                   -1,
742     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
743     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
744   },
745   {
746     EL_BD_MAGIC_WALL,                   -1,
747     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
748     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
749   },
750   {
751     EL_BD_MAGIC_WALL,                   -1,
752     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
753     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
754   },
755   {
756     EL_BD_MAGIC_WALL,                   -1,
757     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
758     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
759   },
760   {
761     EL_BD_MAGIC_WALL,                   -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
763     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
764   },
765   {
766     EL_BD_MAGIC_WALL,                   -1,
767     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
768     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
769   },
770   {
771     EL_BD_MAGIC_WALL,                   -1,
772     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
773     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
774   },
775
776   {
777     EL_BD_CLOCK,                        -1,
778     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
779     &li.bd_clock_extra_time,            30
780   },
781
782   {
783     EL_BD_VOODOO_DOLL,                  -1,
784     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
785     &li.bd_voodoo_collects_diamonds,    FALSE
786   },
787   {
788     EL_BD_VOODOO_DOLL,                  -1,
789     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
790     &li.bd_voodoo_hurt_kills_player,    FALSE
791   },
792   {
793     EL_BD_VOODOO_DOLL,                  -1,
794     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
795     &li.bd_voodoo_dies_by_rock,         FALSE
796   },
797   {
798     EL_BD_VOODOO_DOLL,                  -1,
799     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
800     &li.bd_voodoo_vanish_by_explosion,  TRUE
801   },
802   {
803     EL_BD_VOODOO_DOLL,                  -1,
804     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
805     &li.bd_voodoo_penalty_time,         30
806   },
807
808   {
809     EL_BD_SLIME,                        -1,
810     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
811     &li.bd_slime_is_predictable,        TRUE
812   },
813   {
814     EL_BD_SLIME,                        -1,
815     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
816     &li.bd_slime_permeability_rate,     100
817   },
818   {
819     EL_BD_SLIME,                        -1,
820     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
821     &li.bd_slime_permeability_bits_c64, 0
822   },
823   {
824     EL_BD_SLIME,                        -1,
825     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
826     &li.bd_slime_random_seed_c64,       -1
827   },
828   {
829     EL_BD_SLIME,                        -1,
830     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
831     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
832   },
833   {
834     EL_BD_SLIME,                        -1,
835     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
836     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
837   },
838   {
839     EL_BD_SLIME,                        -1,
840     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
841     &li.bd_slime_eats_element_2,        EL_BD_ROCK
842   },
843   {
844     EL_BD_SLIME,                        -1,
845     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
846     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
847   },
848   {
849     EL_BD_SLIME,                        -1,
850     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
851     &li.bd_slime_eats_element_3,        EL_BD_NUT
852   },
853   {
854     EL_BD_SLIME,                        -1,
855     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
856     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
857   },
858
859   {
860     EL_BD_ACID,                         -1,
861     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
862     &li.bd_acid_eats_element,           EL_BD_SAND
863   },
864   {
865     EL_BD_ACID,                         -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
867     &li.bd_acid_spread_rate,            3
868   },
869   {
870     EL_BD_ACID,                         -1,
871     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
872     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
873   },
874
875   {
876     EL_BD_BITER,                        -1,
877     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
878     &li.bd_biter_move_delay,            0
879   },
880   {
881     EL_BD_BITER,                        -1,
882     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
883     &li.bd_biter_eats_element,          EL_BD_DIAMOND
884   },
885
886   {
887     EL_BD_BLADDER,                      -1,
888     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
889     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
890   },
891
892   {
893     EL_BD_EXPANDABLE_WALL_ANY,          -1,
894     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
895     &li.bd_change_expanding_wall,       FALSE
896   },
897   {
898     EL_BD_EXPANDABLE_WALL_ANY,          -1,
899     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
900     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
901   },
902
903   {
904     EL_BD_REPLICATOR,                   -1,
905     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
906     &li.bd_replicators_active,          TRUE
907   },
908   {
909     EL_BD_REPLICATOR,                   -1,
910     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
911     &li.bd_replicator_create_delay,     4
912   },
913
914   {
915     EL_BD_CONVEYOR_LEFT,                -1,
916     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
917     &li.bd_conveyor_belts_active,       TRUE
918   },
919   {
920     EL_BD_CONVEYOR_LEFT,                -1,
921     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
922     &li.bd_conveyor_belts_changed,      FALSE
923   },
924
925   {
926     EL_BD_WATER,                        -1,
927     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
928     &li.bd_water_cannot_flow_down,      FALSE
929   },
930
931   {
932     EL_BD_NUT,                          -1,
933     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
934     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
935   },
936
937   {
938     EL_BD_PNEUMATIC_HAMMER,             -1,
939     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
940     &li.bd_hammer_walls_break_delay,    5
941   },
942   {
943     EL_BD_PNEUMATIC_HAMMER,             -1,
944     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
945     &li.bd_hammer_walls_reappear,       FALSE
946   },
947   {
948     EL_BD_PNEUMATIC_HAMMER,             -1,
949     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
950     &li.bd_hammer_walls_reappear_delay, 100
951   },
952
953   {
954     EL_BD_ROCKET_LAUNCHER,              -1,
955     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
956     &li.bd_infinite_rockets,            FALSE
957   },
958
959   {
960     EL_BD_SKELETON,                     -1,
961     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
962     &li.bd_num_skeletons_needed_for_pot, 5
963   },
964   {
965     EL_BD_SKELETON,                     -1,
966     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
967     &li.bd_skeleton_worth_num_diamonds, 0
968   },
969
970   {
971     EL_BD_CREATURE_SWITCH,              -1,
972     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
973     &li.bd_creatures_start_backwards,   FALSE
974   },
975   {
976     EL_BD_CREATURE_SWITCH,              -1,
977     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
978     &li.bd_creatures_turn_on_hatching,  FALSE
979   },
980   {
981     EL_BD_CREATURE_SWITCH,              -1,
982     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
983     &li.bd_creatures_auto_turn_delay,   0
984   },
985
986   {
987     EL_BD_GRAVITY_SWITCH,               -1,
988     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
989     &li.bd_gravity_direction,           GD_MV_DOWN
990   },
991   {
992     EL_BD_GRAVITY_SWITCH,               -1,
993     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
994     &li.bd_gravity_switch_active,       FALSE
995   },
996   {
997     EL_BD_GRAVITY_SWITCH,               -1,
998     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
999     &li.bd_gravity_switch_delay,        10
1000   },
1001   {
1002     EL_BD_GRAVITY_SWITCH,               -1,
1003     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1004     &li.bd_gravity_affects_all,         TRUE
1005   },
1006
1007   // (the following values are related to various game elements)
1008
1009   {
1010     EL_EMERALD,                         -1,
1011     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1012     &li.score[SC_EMERALD],              10
1013   },
1014
1015   {
1016     EL_DIAMOND,                         -1,
1017     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1018     &li.score[SC_DIAMOND],              10
1019   },
1020
1021   {
1022     EL_BUG,                             -1,
1023     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1024     &li.score[SC_BUG],                  10
1025   },
1026
1027   {
1028     EL_SPACESHIP,                       -1,
1029     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1030     &li.score[SC_SPACESHIP],            10
1031   },
1032
1033   {
1034     EL_PACMAN,                          -1,
1035     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1036     &li.score[SC_PACMAN],               10
1037   },
1038
1039   {
1040     EL_NUT,                             -1,
1041     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1042     &li.score[SC_NUT],                  10
1043   },
1044
1045   {
1046     EL_DYNAMITE,                        -1,
1047     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1048     &li.score[SC_DYNAMITE],             10
1049   },
1050
1051   {
1052     EL_KEY_1,                           -1,
1053     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1054     &li.score[SC_KEY],                  10
1055   },
1056
1057   {
1058     EL_PEARL,                           -1,
1059     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1060     &li.score[SC_PEARL],                10
1061   },
1062
1063   {
1064     EL_CRYSTAL,                         -1,
1065     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1066     &li.score[SC_CRYSTAL],              10
1067   },
1068
1069   // (amoeba values used by R'n'D game engine only)
1070   {
1071     EL_BD_AMOEBA,                       -1,
1072     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1073     &li.amoeba_content,                 EL_DIAMOND
1074   },
1075   {
1076     EL_BD_AMOEBA,                       -1,
1077     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1078     &li.amoeba_speed,                   10
1079   },
1080   {
1081     EL_BD_AMOEBA,                       -1,
1082     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1083     &li.grow_into_diggable,             TRUE
1084   },
1085   // (amoeba values used by BD game engine only)
1086   {
1087     EL_BD_AMOEBA,                       -1,
1088     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1089     &li.bd_amoeba_wait_for_hatching,    FALSE
1090   },
1091   {
1092     EL_BD_AMOEBA,                       -1,
1093     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1094     &li.bd_amoeba_start_immediately,    TRUE
1095   },
1096   {
1097     EL_BD_AMOEBA,                       -1,
1098     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1099     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1100   },
1101   {
1102     EL_BD_AMOEBA,                       -1,
1103     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1104     &li.bd_amoeba_threshold_too_big,    200
1105   },
1106   {
1107     EL_BD_AMOEBA,                       -1,
1108     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1109     &li.bd_amoeba_slow_growth_time,     200
1110   },
1111   {
1112     EL_BD_AMOEBA,                       -1,
1113     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1114     &li.bd_amoeba_slow_growth_rate,     3
1115   },
1116   {
1117     EL_BD_AMOEBA,                       -1,
1118     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1119     &li.bd_amoeba_fast_growth_rate,     25
1120   },
1121   {
1122     EL_BD_AMOEBA,                       -1,
1123     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1124     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1125   },
1126   {
1127     EL_BD_AMOEBA,                       -1,
1128     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1129     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1130   },
1131
1132   {
1133     EL_BD_AMOEBA_2,                     -1,
1134     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1135     &li.bd_amoeba_2_threshold_too_big,  200
1136   },
1137   {
1138     EL_BD_AMOEBA_2,                     -1,
1139     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1140     &li.bd_amoeba_2_slow_growth_time,   200
1141   },
1142   {
1143     EL_BD_AMOEBA_2,                     -1,
1144     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1145     &li.bd_amoeba_2_slow_growth_rate,   3
1146   },
1147   {
1148     EL_BD_AMOEBA_2,                     -1,
1149     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1150     &li.bd_amoeba_2_fast_growth_rate,   25
1151   },
1152   {
1153     EL_BD_AMOEBA_2,                     -1,
1154     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1155     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1156   },
1157   {
1158     EL_BD_AMOEBA_2,                     -1,
1159     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1160     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1161   },
1162   {
1163     EL_BD_AMOEBA_2,                     -1,
1164     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1165     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1166   },
1167   {
1168     EL_BD_AMOEBA_2,                     -1,
1169     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1170     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1171   },
1172
1173   {
1174     EL_YAMYAM,                          -1,
1175     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1176     &li.yamyam_content,                 EL_ROCK, NULL,
1177     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1178   },
1179   {
1180     EL_YAMYAM,                          -1,
1181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1182     &li.score[SC_YAMYAM],               10
1183   },
1184
1185   {
1186     EL_ROBOT,                           -1,
1187     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1188     &li.score[SC_ROBOT],                10
1189   },
1190   {
1191     EL_ROBOT,                           -1,
1192     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1193     &li.slurp_score,                    10
1194   },
1195
1196   {
1197     EL_ROBOT_WHEEL,                     -1,
1198     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1199     &li.time_wheel,                     10
1200   },
1201
1202   {
1203     EL_MAGIC_WALL,                      -1,
1204     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1205     &li.time_magic_wall,                10
1206   },
1207
1208   {
1209     EL_GAME_OF_LIFE,                    -1,
1210     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1211     &li.game_of_life[0],                2
1212   },
1213   {
1214     EL_GAME_OF_LIFE,                    -1,
1215     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1216     &li.game_of_life[1],                3
1217   },
1218   {
1219     EL_GAME_OF_LIFE,                    -1,
1220     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1221     &li.game_of_life[2],                3
1222   },
1223   {
1224     EL_GAME_OF_LIFE,                    -1,
1225     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1226     &li.game_of_life[3],                3
1227   },
1228   {
1229     EL_GAME_OF_LIFE,                    -1,
1230     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1231     &li.use_life_bugs,                  FALSE
1232   },
1233
1234   {
1235     EL_BIOMAZE,                         -1,
1236     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1237     &li.biomaze[0],                     2
1238   },
1239   {
1240     EL_BIOMAZE,                         -1,
1241     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1242     &li.biomaze[1],                     3
1243   },
1244   {
1245     EL_BIOMAZE,                         -1,
1246     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1247     &li.biomaze[2],                     3
1248   },
1249   {
1250     EL_BIOMAZE,                         -1,
1251     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1252     &li.biomaze[3],                     3
1253   },
1254
1255   {
1256     EL_TIMEGATE_SWITCH,                 -1,
1257     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1258     &li.time_timegate,                  10
1259   },
1260
1261   {
1262     EL_LIGHT_SWITCH_ACTIVE,             -1,
1263     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1264     &li.time_light,                     10
1265   },
1266
1267   {
1268     EL_SHIELD_NORMAL,                   -1,
1269     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1270     &li.shield_normal_time,             10
1271   },
1272   {
1273     EL_SHIELD_NORMAL,                   -1,
1274     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1275     &li.score[SC_SHIELD],               10
1276   },
1277
1278   {
1279     EL_SHIELD_DEADLY,                   -1,
1280     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1281     &li.shield_deadly_time,             10
1282   },
1283   {
1284     EL_SHIELD_DEADLY,                   -1,
1285     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1286     &li.score[SC_SHIELD],               10
1287   },
1288
1289   {
1290     EL_EXTRA_TIME,                      -1,
1291     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1292     &li.extra_time,                     10
1293   },
1294   {
1295     EL_EXTRA_TIME,                      -1,
1296     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1297     &li.extra_time_score,               10
1298   },
1299
1300   {
1301     EL_TIME_ORB_FULL,                   -1,
1302     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1303     &li.time_orb_time,                  10
1304   },
1305   {
1306     EL_TIME_ORB_FULL,                   -1,
1307     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1308     &li.use_time_orb_bug,               FALSE
1309   },
1310
1311   {
1312     EL_SPRING,                          -1,
1313     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1314     &li.use_spring_bug,                 FALSE
1315   },
1316
1317   {
1318     EL_EMC_ANDROID,                     -1,
1319     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1320     &li.android_move_time,              10
1321   },
1322   {
1323     EL_EMC_ANDROID,                     -1,
1324     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1325     &li.android_clone_time,             10
1326   },
1327   {
1328     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1329     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1330     &li.android_clone_element[0],       EL_EMPTY, NULL,
1331     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1332   },
1333   {
1334     EL_EMC_ANDROID,                     -1,
1335     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1336     &li.android_clone_element[0],       EL_EMPTY, NULL,
1337     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1338   },
1339
1340   {
1341     EL_EMC_LENSES,                      -1,
1342     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1343     &li.lenses_score,                   10
1344   },
1345   {
1346     EL_EMC_LENSES,                      -1,
1347     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1348     &li.lenses_time,                    10
1349   },
1350
1351   {
1352     EL_EMC_MAGNIFIER,                   -1,
1353     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1354     &li.magnify_score,                  10
1355   },
1356   {
1357     EL_EMC_MAGNIFIER,                   -1,
1358     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1359     &li.magnify_time,                   10
1360   },
1361
1362   {
1363     EL_EMC_MAGIC_BALL,                  -1,
1364     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1365     &li.ball_time,                      10
1366   },
1367   {
1368     EL_EMC_MAGIC_BALL,                  -1,
1369     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1370     &li.ball_random,                    FALSE
1371   },
1372   {
1373     EL_EMC_MAGIC_BALL,                  -1,
1374     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1375     &li.ball_active_initial,            FALSE
1376   },
1377   {
1378     EL_EMC_MAGIC_BALL,                  -1,
1379     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1380     &li.ball_content,                   EL_EMPTY, NULL,
1381     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1382   },
1383
1384   {
1385     EL_SOKOBAN_FIELD_EMPTY,             -1,
1386     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1387     &li.sb_fields_needed,               TRUE
1388   },
1389
1390   {
1391     EL_SOKOBAN_OBJECT,                  -1,
1392     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1393     &li.sb_objects_needed,              TRUE
1394   },
1395
1396   {
1397     EL_MM_MCDUFFIN,                     -1,
1398     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1399     &li.mm_laser_red,                   FALSE
1400   },
1401   {
1402     EL_MM_MCDUFFIN,                     -1,
1403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1404     &li.mm_laser_green,                 FALSE
1405   },
1406   {
1407     EL_MM_MCDUFFIN,                     -1,
1408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1409     &li.mm_laser_blue,                  TRUE
1410   },
1411
1412   {
1413     EL_DF_LASER,                        -1,
1414     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1415     &li.df_laser_red,                   TRUE
1416   },
1417   {
1418     EL_DF_LASER,                        -1,
1419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1420     &li.df_laser_green,                 TRUE
1421   },
1422   {
1423     EL_DF_LASER,                        -1,
1424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1425     &li.df_laser_blue,                  FALSE
1426   },
1427
1428   {
1429     EL_MM_FUSE_ACTIVE,                  -1,
1430     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1431     &li.mm_time_fuse,                   25
1432   },
1433   {
1434     EL_MM_BOMB,                         -1,
1435     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1436     &li.mm_time_bomb,                   75
1437   },
1438
1439   {
1440     EL_MM_GRAY_BALL,                    -1,
1441     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1442     &li.mm_time_ball,                   75
1443   },
1444   {
1445     EL_MM_GRAY_BALL,                    -1,
1446     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1447     &li.mm_ball_choice_mode,            ANIM_RANDOM
1448   },
1449   {
1450     EL_MM_GRAY_BALL,                    -1,
1451     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1452     &li.mm_ball_content,                EL_EMPTY, NULL,
1453     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1454   },
1455   {
1456     EL_MM_GRAY_BALL,                    -1,
1457     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1458     &li.rotate_mm_ball_content,         TRUE
1459   },
1460   {
1461     EL_MM_GRAY_BALL,                    -1,
1462     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1463     &li.explode_mm_ball,                FALSE
1464   },
1465
1466   {
1467     EL_MM_STEEL_BLOCK,                  -1,
1468     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1469     &li.mm_time_block,                  75
1470   },
1471   {
1472     EL_MM_LIGHTBALL,                    -1,
1473     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1474     &li.score[SC_ELEM_BONUS],           10
1475   },
1476
1477   {
1478     -1,                                 -1,
1479     -1,                                 -1,
1480     NULL,                               -1
1481   }
1482 };
1483
1484 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1485 {
1486   {
1487     -1,                                 -1,
1488     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1489     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1490   },
1491   {
1492     -1,                                 -1,
1493     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1494     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1495   },
1496
1497   {
1498     -1,                                 -1,
1499     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1500     &xx_envelope.autowrap,              FALSE
1501   },
1502   {
1503     -1,                                 -1,
1504     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1505     &xx_envelope.centered,              FALSE
1506   },
1507
1508   {
1509     -1,                                 -1,
1510     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1511     &xx_envelope.text,                  -1, NULL,
1512     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1513     &xx_default_string_empty[0]
1514   },
1515
1516   {
1517     -1,                                 -1,
1518     -1,                                 -1,
1519     NULL,                               -1
1520   }
1521 };
1522
1523 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1524 {
1525   {
1526     -1,                                 -1,
1527     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1528     &xx_ei.description[0],              -1,
1529     &yy_ei.description[0],
1530     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1531     &xx_default_description[0]
1532   },
1533
1534   {
1535     -1,                                 -1,
1536     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1537     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1538     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1539   },
1540 #if ENABLE_RESERVED_CODE
1541   // (reserved for later use)
1542   {
1543     -1,                                 -1,
1544     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1545     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1546     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1547   },
1548 #endif
1549
1550   {
1551     -1,                                 -1,
1552     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1553     &xx_ei.use_gfx_element,             FALSE,
1554     &yy_ei.use_gfx_element
1555   },
1556   {
1557     -1,                                 -1,
1558     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1559     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1560     &yy_ei.gfx_element_initial
1561   },
1562
1563   {
1564     -1,                                 -1,
1565     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1566     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1567     &yy_ei.access_direction
1568   },
1569
1570   {
1571     -1,                                 -1,
1572     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1573     &xx_ei.collect_score_initial,       10,
1574     &yy_ei.collect_score_initial
1575   },
1576   {
1577     -1,                                 -1,
1578     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1579     &xx_ei.collect_count_initial,       1,
1580     &yy_ei.collect_count_initial
1581   },
1582
1583   {
1584     -1,                                 -1,
1585     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1586     &xx_ei.ce_value_fixed_initial,      0,
1587     &yy_ei.ce_value_fixed_initial
1588   },
1589   {
1590     -1,                                 -1,
1591     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1592     &xx_ei.ce_value_random_initial,     0,
1593     &yy_ei.ce_value_random_initial
1594   },
1595   {
1596     -1,                                 -1,
1597     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1598     &xx_ei.use_last_ce_value,           FALSE,
1599     &yy_ei.use_last_ce_value
1600   },
1601
1602   {
1603     -1,                                 -1,
1604     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1605     &xx_ei.push_delay_fixed,            8,
1606     &yy_ei.push_delay_fixed
1607   },
1608   {
1609     -1,                                 -1,
1610     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1611     &xx_ei.push_delay_random,           8,
1612     &yy_ei.push_delay_random
1613   },
1614   {
1615     -1,                                 -1,
1616     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1617     &xx_ei.drop_delay_fixed,            0,
1618     &yy_ei.drop_delay_fixed
1619   },
1620   {
1621     -1,                                 -1,
1622     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1623     &xx_ei.drop_delay_random,           0,
1624     &yy_ei.drop_delay_random
1625   },
1626   {
1627     -1,                                 -1,
1628     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1629     &xx_ei.move_delay_fixed,            0,
1630     &yy_ei.move_delay_fixed
1631   },
1632   {
1633     -1,                                 -1,
1634     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1635     &xx_ei.move_delay_random,           0,
1636     &yy_ei.move_delay_random
1637   },
1638   {
1639     -1,                                 -1,
1640     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1641     &xx_ei.step_delay_fixed,            0,
1642     &yy_ei.step_delay_fixed
1643   },
1644   {
1645     -1,                                 -1,
1646     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1647     &xx_ei.step_delay_random,           0,
1648     &yy_ei.step_delay_random
1649   },
1650
1651   {
1652     -1,                                 -1,
1653     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1654     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1655     &yy_ei.move_pattern
1656   },
1657   {
1658     -1,                                 -1,
1659     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1660     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1661     &yy_ei.move_direction_initial
1662   },
1663   {
1664     -1,                                 -1,
1665     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1666     &xx_ei.move_stepsize,               TILEX / 8,
1667     &yy_ei.move_stepsize
1668   },
1669
1670   {
1671     -1,                                 -1,
1672     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1673     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1674     &yy_ei.move_enter_element
1675   },
1676   {
1677     -1,                                 -1,
1678     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1679     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1680     &yy_ei.move_leave_element
1681   },
1682   {
1683     -1,                                 -1,
1684     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1685     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1686     &yy_ei.move_leave_type
1687   },
1688
1689   {
1690     -1,                                 -1,
1691     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1692     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1693     &yy_ei.slippery_type
1694   },
1695
1696   {
1697     -1,                                 -1,
1698     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1699     &xx_ei.explosion_type,              EXPLODES_3X3,
1700     &yy_ei.explosion_type
1701   },
1702   {
1703     -1,                                 -1,
1704     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1705     &xx_ei.explosion_delay,             16,
1706     &yy_ei.explosion_delay
1707   },
1708   {
1709     -1,                                 -1,
1710     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1711     &xx_ei.ignition_delay,              8,
1712     &yy_ei.ignition_delay
1713   },
1714
1715   {
1716     -1,                                 -1,
1717     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1718     &xx_ei.content,                     EL_EMPTY_SPACE,
1719     &yy_ei.content,
1720     &xx_num_contents,                   1, 1
1721   },
1722
1723   // ---------- "num_change_pages" must be the last entry ---------------------
1724
1725   {
1726     -1,                                 SAVE_CONF_ALWAYS,
1727     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1728     &xx_ei.num_change_pages,            1,
1729     &yy_ei.num_change_pages
1730   },
1731
1732   {
1733     -1,                                 -1,
1734     -1,                                 -1,
1735     NULL,                               -1,
1736     NULL
1737   }
1738 };
1739
1740 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1741 {
1742   // ---------- "current_change_page" must be the first entry -----------------
1743
1744   {
1745     -1,                                 SAVE_CONF_ALWAYS,
1746     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1747     &xx_current_change_page,            -1
1748   },
1749
1750   // ---------- (the remaining entries can be in any order) -------------------
1751
1752   {
1753     -1,                                 -1,
1754     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1755     &xx_change.can_change,              FALSE
1756   },
1757
1758   {
1759     -1,                                 -1,
1760     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1761     &xx_event_bits[0],                  0
1762   },
1763   {
1764     -1,                                 -1,
1765     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1766     &xx_event_bits[1],                  0
1767   },
1768
1769   {
1770     -1,                                 -1,
1771     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1772     &xx_change.trigger_player,          CH_PLAYER_ANY
1773   },
1774   {
1775     -1,                                 -1,
1776     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1777     &xx_change.trigger_side,            CH_SIDE_ANY
1778   },
1779   {
1780     -1,                                 -1,
1781     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1782     &xx_change.trigger_page,            CH_PAGE_ANY
1783   },
1784
1785   {
1786     -1,                                 -1,
1787     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1788     &xx_change.target_element,          EL_EMPTY_SPACE
1789   },
1790
1791   {
1792     -1,                                 -1,
1793     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1794     &xx_change.delay_fixed,             0
1795   },
1796   {
1797     -1,                                 -1,
1798     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1799     &xx_change.delay_random,            0
1800   },
1801   {
1802     -1,                                 -1,
1803     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1804     &xx_change.delay_frames,            FRAMES_PER_SECOND
1805   },
1806
1807   {
1808     -1,                                 -1,
1809     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1810     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1811   },
1812
1813   {
1814     -1,                                 -1,
1815     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1816     &xx_change.explode,                 FALSE
1817   },
1818   {
1819     -1,                                 -1,
1820     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1821     &xx_change.use_target_content,      FALSE
1822   },
1823   {
1824     -1,                                 -1,
1825     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1826     &xx_change.only_if_complete,        FALSE
1827   },
1828   {
1829     -1,                                 -1,
1830     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1831     &xx_change.use_random_replace,      FALSE
1832   },
1833   {
1834     -1,                                 -1,
1835     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1836     &xx_change.random_percentage,       100
1837   },
1838   {
1839     -1,                                 -1,
1840     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1841     &xx_change.replace_when,            CP_WHEN_EMPTY
1842   },
1843
1844   {
1845     -1,                                 -1,
1846     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1847     &xx_change.has_action,              FALSE
1848   },
1849   {
1850     -1,                                 -1,
1851     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1852     &xx_change.action_type,             CA_NO_ACTION
1853   },
1854   {
1855     -1,                                 -1,
1856     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1857     &xx_change.action_mode,             CA_MODE_UNDEFINED
1858   },
1859   {
1860     -1,                                 -1,
1861     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1862     &xx_change.action_arg,              CA_ARG_UNDEFINED
1863   },
1864
1865   {
1866     -1,                                 -1,
1867     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1868     &xx_change.action_element,          EL_EMPTY_SPACE
1869   },
1870
1871   {
1872     -1,                                 -1,
1873     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1874     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1875     &xx_num_contents,                   1, 1
1876   },
1877
1878   {
1879     -1,                                 -1,
1880     -1,                                 -1,
1881     NULL,                               -1
1882   }
1883 };
1884
1885 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1886 {
1887   {
1888     -1,                                 -1,
1889     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1890     &xx_ei.description[0],              -1, NULL,
1891     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1892     &xx_default_description[0]
1893   },
1894
1895   {
1896     -1,                                 -1,
1897     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1898     &xx_ei.use_gfx_element,             FALSE
1899   },
1900   {
1901     -1,                                 -1,
1902     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1903     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1904   },
1905
1906   {
1907     -1,                                 -1,
1908     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1909     &xx_group.choice_mode,              ANIM_RANDOM
1910   },
1911
1912   {
1913     -1,                                 -1,
1914     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1915     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1916     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1917   },
1918
1919   {
1920     -1,                                 -1,
1921     -1,                                 -1,
1922     NULL,                               -1
1923   }
1924 };
1925
1926 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1927 {
1928   {
1929     -1,                                 -1,
1930     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1931     &xx_ei.use_gfx_element,             FALSE
1932   },
1933   {
1934     -1,                                 -1,
1935     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1936     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1937   },
1938
1939   {
1940     -1,                                 -1,
1941     -1,                                 -1,
1942     NULL,                               -1
1943   }
1944 };
1945
1946 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1947 {
1948   {
1949     EL_PLAYER_1,                        -1,
1950     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1951     &li.block_snap_field,               TRUE
1952   },
1953   {
1954     EL_PLAYER_1,                        -1,
1955     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1956     &li.continuous_snapping,            TRUE
1957   },
1958   {
1959     EL_PLAYER_1,                        -1,
1960     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1961     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1962   },
1963   {
1964     EL_PLAYER_1,                        -1,
1965     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1966     &li.use_start_element[0],           FALSE
1967   },
1968   {
1969     EL_PLAYER_1,                        -1,
1970     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1971     &li.start_element[0],               EL_PLAYER_1
1972   },
1973   {
1974     EL_PLAYER_1,                        -1,
1975     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1976     &li.use_artwork_element[0],         FALSE
1977   },
1978   {
1979     EL_PLAYER_1,                        -1,
1980     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1981     &li.artwork_element[0],             EL_PLAYER_1
1982   },
1983   {
1984     EL_PLAYER_1,                        -1,
1985     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1986     &li.use_explosion_element[0],       FALSE
1987   },
1988   {
1989     EL_PLAYER_1,                        -1,
1990     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1991     &li.explosion_element[0],           EL_PLAYER_1
1992   },
1993
1994   {
1995     -1,                                 -1,
1996     -1,                                 -1,
1997     NULL,                               -1
1998   }
1999 };
2000
2001 static struct
2002 {
2003   int filetype;
2004   char *id;
2005 }
2006 filetype_id_list[] =
2007 {
2008   { LEVEL_FILE_TYPE_RND,        "RND"   },
2009   { LEVEL_FILE_TYPE_BD,         "BD"    },
2010   { LEVEL_FILE_TYPE_EM,         "EM"    },
2011   { LEVEL_FILE_TYPE_SP,         "SP"    },
2012   { LEVEL_FILE_TYPE_DX,         "DX"    },
2013   { LEVEL_FILE_TYPE_SB,         "SB"    },
2014   { LEVEL_FILE_TYPE_DC,         "DC"    },
2015   { LEVEL_FILE_TYPE_MM,         "MM"    },
2016   { LEVEL_FILE_TYPE_MM,         "DF"    },
2017   { -1,                         NULL    },
2018 };
2019
2020
2021 // ============================================================================
2022 // level file functions
2023 // ============================================================================
2024
2025 static boolean check_special_flags(char *flag)
2026 {
2027   if (strEqual(options.special_flags, flag) ||
2028       strEqual(leveldir_current->special_flags, flag))
2029     return TRUE;
2030
2031   return FALSE;
2032 }
2033
2034 static struct DateInfo getCurrentDate(void)
2035 {
2036   time_t epoch_seconds = time(NULL);
2037   struct tm *now = localtime(&epoch_seconds);
2038   struct DateInfo date;
2039
2040   date.year  = now->tm_year + 1900;
2041   date.month = now->tm_mon  + 1;
2042   date.day   = now->tm_mday;
2043
2044   date.src   = DATE_SRC_CLOCK;
2045
2046   return date;
2047 }
2048
2049 static void resetEventFlags(struct ElementChangeInfo *change)
2050 {
2051   int i;
2052
2053   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2054     change->has_event[i] = FALSE;
2055 }
2056
2057 static void resetEventBits(void)
2058 {
2059   int i;
2060
2061   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2062     xx_event_bits[i] = 0;
2063 }
2064
2065 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2066 {
2067   int i;
2068
2069   /* important: only change event flag if corresponding event bit is set
2070      (this is because all xx_event_bits[] values are loaded separately,
2071      and all xx_event_bits[] values are set back to zero before loading
2072      another value xx_event_bits[x] (each value representing 32 flags)) */
2073
2074   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2075     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2076       change->has_event[i] = TRUE;
2077 }
2078
2079 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2080 {
2081   int i;
2082
2083   /* in contrast to the above function setEventFlagsFromEventBits(), it
2084      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2085      depending on the corresponding change->has_event[i] values here, as
2086      all xx_event_bits[] values are reset in resetEventBits() before */
2087
2088   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2089     if (change->has_event[i])
2090       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2091 }
2092
2093 static char *getDefaultElementDescription(struct ElementInfo *ei)
2094 {
2095   static char description[MAX_ELEMENT_NAME_LEN + 1];
2096   char *default_description = (ei->custom_description != NULL ?
2097                                ei->custom_description :
2098                                ei->editor_description);
2099   int i;
2100
2101   // always start with reliable default values
2102   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2103     description[i] = '\0';
2104
2105   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2106   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2107
2108   return &description[0];
2109 }
2110
2111 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2112 {
2113   char *default_description = getDefaultElementDescription(ei);
2114   int i;
2115
2116   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2117     ei->description[i] = default_description[i];
2118 }
2119
2120 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2121 {
2122   int i;
2123
2124   for (i = 0; conf[i].data_type != -1; i++)
2125   {
2126     int default_value = conf[i].default_value;
2127     int data_type = conf[i].data_type;
2128     int conf_type = conf[i].conf_type;
2129     int byte_mask = conf_type & CONF_MASK_BYTES;
2130
2131     if (byte_mask == CONF_MASK_MULTI_BYTES)
2132     {
2133       int default_num_entities = conf[i].default_num_entities;
2134       int max_num_entities = conf[i].max_num_entities;
2135
2136       *(int *)(conf[i].num_entities) = default_num_entities;
2137
2138       if (data_type == TYPE_STRING)
2139       {
2140         char *default_string = conf[i].default_string;
2141         char *string = (char *)(conf[i].value);
2142
2143         strncpy(string, default_string, max_num_entities);
2144       }
2145       else if (data_type == TYPE_ELEMENT_LIST)
2146       {
2147         int *element_array = (int *)(conf[i].value);
2148         int j;
2149
2150         for (j = 0; j < max_num_entities; j++)
2151           element_array[j] = default_value;
2152       }
2153       else if (data_type == TYPE_CONTENT_LIST)
2154       {
2155         struct Content *content = (struct Content *)(conf[i].value);
2156         int c, x, y;
2157
2158         for (c = 0; c < max_num_entities; c++)
2159           for (y = 0; y < 3; y++)
2160             for (x = 0; x < 3; x++)
2161               content[c].e[x][y] = default_value;
2162       }
2163     }
2164     else        // constant size configuration data (1, 2 or 4 bytes)
2165     {
2166       if (data_type == TYPE_BOOLEAN)
2167         *(boolean *)(conf[i].value) = default_value;
2168       else
2169         *(int *)    (conf[i].value) = default_value;
2170     }
2171   }
2172 }
2173
2174 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2175 {
2176   int i;
2177
2178   for (i = 0; conf[i].data_type != -1; i++)
2179   {
2180     int data_type = conf[i].data_type;
2181     int conf_type = conf[i].conf_type;
2182     int byte_mask = conf_type & CONF_MASK_BYTES;
2183
2184     if (byte_mask == CONF_MASK_MULTI_BYTES)
2185     {
2186       int max_num_entities = conf[i].max_num_entities;
2187
2188       if (data_type == TYPE_STRING)
2189       {
2190         char *string      = (char *)(conf[i].value);
2191         char *string_copy = (char *)(conf[i].value_copy);
2192
2193         strncpy(string_copy, string, max_num_entities);
2194       }
2195       else if (data_type == TYPE_ELEMENT_LIST)
2196       {
2197         int *element_array      = (int *)(conf[i].value);
2198         int *element_array_copy = (int *)(conf[i].value_copy);
2199         int j;
2200
2201         for (j = 0; j < max_num_entities; j++)
2202           element_array_copy[j] = element_array[j];
2203       }
2204       else if (data_type == TYPE_CONTENT_LIST)
2205       {
2206         struct Content *content      = (struct Content *)(conf[i].value);
2207         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2208         int c, x, y;
2209
2210         for (c = 0; c < max_num_entities; c++)
2211           for (y = 0; y < 3; y++)
2212             for (x = 0; x < 3; x++)
2213               content_copy[c].e[x][y] = content[c].e[x][y];
2214       }
2215     }
2216     else        // constant size configuration data (1, 2 or 4 bytes)
2217     {
2218       if (data_type == TYPE_BOOLEAN)
2219         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2220       else
2221         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2222     }
2223   }
2224 }
2225
2226 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2227 {
2228   int i;
2229
2230   xx_ei = *ei_from;     // copy element data into temporary buffer
2231   yy_ei = *ei_to;       // copy element data into temporary buffer
2232
2233   copyConfigFromConfigList(chunk_config_CUSX_base);
2234
2235   *ei_from = xx_ei;
2236   *ei_to   = yy_ei;
2237
2238   // ---------- reinitialize and copy change pages ----------
2239
2240   ei_to->num_change_pages = ei_from->num_change_pages;
2241   ei_to->current_change_page = ei_from->current_change_page;
2242
2243   setElementChangePages(ei_to, ei_to->num_change_pages);
2244
2245   for (i = 0; i < ei_to->num_change_pages; i++)
2246     ei_to->change_page[i] = ei_from->change_page[i];
2247
2248   // ---------- copy group element info ----------
2249   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2250     *ei_to->group = *ei_from->group;
2251
2252   // mark this custom element as modified
2253   ei_to->modified_settings = TRUE;
2254 }
2255
2256 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2257 {
2258   int change_page_size = sizeof(struct ElementChangeInfo);
2259
2260   ei->num_change_pages = MAX(1, change_pages);
2261
2262   ei->change_page =
2263     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2264
2265   if (ei->current_change_page >= ei->num_change_pages)
2266     ei->current_change_page = ei->num_change_pages - 1;
2267
2268   ei->change = &ei->change_page[ei->current_change_page];
2269 }
2270
2271 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2272 {
2273   xx_change = *change;          // copy change data into temporary buffer
2274
2275   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2276
2277   *change = xx_change;
2278
2279   resetEventFlags(change);
2280
2281   change->direct_action = 0;
2282   change->other_action = 0;
2283
2284   change->pre_change_function = NULL;
2285   change->change_function = NULL;
2286   change->post_change_function = NULL;
2287 }
2288
2289 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2290 {
2291   int i, x, y;
2292
2293   li = *level;          // copy level data into temporary buffer
2294   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2295   *level = li;          // copy temporary buffer back to level data
2296
2297   setLevelInfoToDefaults_BD();
2298   setLevelInfoToDefaults_EM();
2299   setLevelInfoToDefaults_SP();
2300   setLevelInfoToDefaults_MM();
2301
2302   level->native_bd_level = &native_bd_level;
2303   level->native_em_level = &native_em_level;
2304   level->native_sp_level = &native_sp_level;
2305   level->native_mm_level = &native_mm_level;
2306
2307   level->file_version = FILE_VERSION_ACTUAL;
2308   level->game_version = GAME_VERSION_ACTUAL;
2309
2310   level->creation_date = getCurrentDate();
2311
2312   level->encoding_16bit_field  = TRUE;
2313   level->encoding_16bit_yamyam = TRUE;
2314   level->encoding_16bit_amoeba = TRUE;
2315
2316   // clear level name and level author string buffers
2317   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2318     level->name[i] = '\0';
2319   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2320     level->author[i] = '\0';
2321
2322   // set level name and level author to default values
2323   strcpy(level->name, NAMELESS_LEVEL_NAME);
2324   strcpy(level->author, ANONYMOUS_NAME);
2325
2326   // set level playfield to playable default level with player and exit
2327   for (x = 0; x < MAX_LEV_FIELDX; x++)
2328     for (y = 0; y < MAX_LEV_FIELDY; y++)
2329       level->field[x][y] = EL_SAND;
2330
2331   level->field[0][0] = EL_PLAYER_1;
2332   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2333
2334   BorderElement = EL_STEELWALL;
2335
2336   // detect custom elements when loading them
2337   level->file_has_custom_elements = FALSE;
2338
2339   // set all bug compatibility flags to "false" => do not emulate this bug
2340   level->use_action_after_change_bug = FALSE;
2341
2342   if (leveldir_current)
2343   {
2344     // try to determine better author name than 'anonymous'
2345     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2346     {
2347       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2348       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2349     }
2350     else
2351     {
2352       switch (LEVELCLASS(leveldir_current))
2353       {
2354         case LEVELCLASS_TUTORIAL:
2355           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2356           break;
2357
2358         case LEVELCLASS_CONTRIB:
2359           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2360           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2361           break;
2362
2363         case LEVELCLASS_PRIVATE:
2364           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2365           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2366           break;
2367
2368         default:
2369           // keep default value
2370           break;
2371       }
2372     }
2373   }
2374 }
2375
2376 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2377 {
2378   static boolean clipboard_elements_initialized = FALSE;
2379   int i;
2380
2381   InitElementPropertiesStatic();
2382
2383   li = *level;          // copy level data into temporary buffer
2384   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2385   *level = li;          // copy temporary buffer back to level data
2386
2387   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2388   {
2389     int element = i;
2390     struct ElementInfo *ei = &element_info[element];
2391
2392     if (element == EL_MM_GRAY_BALL)
2393     {
2394       struct LevelInfo_MM *level_mm = level->native_mm_level;
2395       int j;
2396
2397       for (j = 0; j < level->num_mm_ball_contents; j++)
2398         level->mm_ball_content[j] =
2399           map_element_MM_to_RND(level_mm->ball_content[j]);
2400     }
2401
2402     // never initialize clipboard elements after the very first time
2403     // (to be able to use clipboard elements between several levels)
2404     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2405       continue;
2406
2407     if (IS_ENVELOPE(element))
2408     {
2409       int envelope_nr = element - EL_ENVELOPE_1;
2410
2411       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2412
2413       level->envelope[envelope_nr] = xx_envelope;
2414     }
2415
2416     if (IS_CUSTOM_ELEMENT(element) ||
2417         IS_GROUP_ELEMENT(element) ||
2418         IS_INTERNAL_ELEMENT(element))
2419     {
2420       xx_ei = *ei;      // copy element data into temporary buffer
2421
2422       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2423
2424       *ei = xx_ei;
2425     }
2426
2427     setElementChangePages(ei, 1);
2428     setElementChangeInfoToDefaults(ei->change);
2429
2430     if (IS_CUSTOM_ELEMENT(element) ||
2431         IS_GROUP_ELEMENT(element))
2432     {
2433       setElementDescriptionToDefault(ei);
2434
2435       ei->modified_settings = FALSE;
2436     }
2437
2438     if (IS_CUSTOM_ELEMENT(element) ||
2439         IS_INTERNAL_ELEMENT(element))
2440     {
2441       // internal values used in level editor
2442
2443       ei->access_type = 0;
2444       ei->access_layer = 0;
2445       ei->access_protected = 0;
2446       ei->walk_to_action = 0;
2447       ei->smash_targets = 0;
2448       ei->deadliness = 0;
2449
2450       ei->can_explode_by_fire = FALSE;
2451       ei->can_explode_smashed = FALSE;
2452       ei->can_explode_impact = FALSE;
2453
2454       ei->current_change_page = 0;
2455     }
2456
2457     if (IS_GROUP_ELEMENT(element) ||
2458         IS_INTERNAL_ELEMENT(element))
2459     {
2460       struct ElementGroupInfo *group;
2461
2462       // initialize memory for list of elements in group
2463       if (ei->group == NULL)
2464         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2465
2466       group = ei->group;
2467
2468       xx_group = *group;        // copy group data into temporary buffer
2469
2470       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2471
2472       *group = xx_group;
2473     }
2474
2475     if (IS_EMPTY_ELEMENT(element) ||
2476         IS_INTERNAL_ELEMENT(element))
2477     {
2478       xx_ei = *ei;              // copy element data into temporary buffer
2479
2480       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2481
2482       *ei = xx_ei;
2483     }
2484   }
2485
2486   clipboard_elements_initialized = TRUE;
2487 }
2488
2489 static void setLevelInfoToDefaults(struct LevelInfo *level,
2490                                    boolean level_info_only,
2491                                    boolean reset_file_status)
2492 {
2493   setLevelInfoToDefaults_Level(level);
2494
2495   if (!level_info_only)
2496     setLevelInfoToDefaults_Elements(level);
2497
2498   if (reset_file_status)
2499   {
2500     level->no_valid_file = FALSE;
2501     level->no_level_file = FALSE;
2502   }
2503
2504   level->changed = FALSE;
2505 }
2506
2507 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2508 {
2509   level_file_info->nr = 0;
2510   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2511   level_file_info->packed = FALSE;
2512
2513   setString(&level_file_info->basename, NULL);
2514   setString(&level_file_info->filename, NULL);
2515 }
2516
2517 int getMappedElement_SB(int, boolean);
2518
2519 static void ActivateLevelTemplate(void)
2520 {
2521   int x, y;
2522
2523   if (check_special_flags("load_xsb_to_ces"))
2524   {
2525     // fill smaller playfields with padding "beyond border wall" elements
2526     if (level.fieldx < level_template.fieldx ||
2527         level.fieldy < level_template.fieldy)
2528     {
2529       short field[level.fieldx][level.fieldy];
2530       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2531       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2532       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2533       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2534
2535       // copy old playfield (which is smaller than the visible area)
2536       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2537         field[x][y] = level.field[x][y];
2538
2539       // fill new, larger playfield with "beyond border wall" elements
2540       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2541         level.field[x][y] = getMappedElement_SB('_', TRUE);
2542
2543       // copy the old playfield to the middle of the new playfield
2544       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2545         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2546
2547       level.fieldx = new_fieldx;
2548       level.fieldy = new_fieldy;
2549     }
2550   }
2551
2552   // Currently there is no special action needed to activate the template
2553   // data, because 'element_info' property settings overwrite the original
2554   // level data, while all other variables do not change.
2555
2556   // Exception: 'from_level_template' elements in the original level playfield
2557   // are overwritten with the corresponding elements at the same position in
2558   // playfield from the level template.
2559
2560   for (x = 0; x < level.fieldx; x++)
2561     for (y = 0; y < level.fieldy; y++)
2562       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2563         level.field[x][y] = level_template.field[x][y];
2564
2565   if (check_special_flags("load_xsb_to_ces"))
2566   {
2567     struct LevelInfo level_backup = level;
2568
2569     // overwrite all individual level settings from template level settings
2570     level = level_template;
2571
2572     // restore level file info
2573     level.file_info = level_backup.file_info;
2574
2575     // restore playfield size
2576     level.fieldx = level_backup.fieldx;
2577     level.fieldy = level_backup.fieldy;
2578
2579     // restore playfield content
2580     for (x = 0; x < level.fieldx; x++)
2581       for (y = 0; y < level.fieldy; y++)
2582         level.field[x][y] = level_backup.field[x][y];
2583
2584     // restore name and author from individual level
2585     strcpy(level.name,   level_backup.name);
2586     strcpy(level.author, level_backup.author);
2587
2588     // restore flag "use_custom_template"
2589     level.use_custom_template = level_backup.use_custom_template;
2590   }
2591 }
2592
2593 static boolean checkForPackageFromBasename_BD(char *basename)
2594 {
2595   // check for native BD level file extensions
2596   if (!strSuffixLower(basename, ".bd") &&
2597       !strSuffixLower(basename, ".bdr") &&
2598       !strSuffixLower(basename, ".brc") &&
2599       !strSuffixLower(basename, ".gds"))
2600     return FALSE;
2601
2602   // check for standard single-level BD files (like "001.bd")
2603   if (strSuffixLower(basename, ".bd") &&
2604       strlen(basename) == 6 &&
2605       basename[0] >= '0' && basename[0] <= '9' &&
2606       basename[1] >= '0' && basename[1] <= '9' &&
2607       basename[2] >= '0' && basename[2] <= '9')
2608     return FALSE;
2609
2610   // this is a level package in native BD file format
2611   return TRUE;
2612 }
2613
2614 static char *getLevelFilenameFromBasename(char *basename)
2615 {
2616   static char *filename = NULL;
2617
2618   checked_free(filename);
2619
2620   filename = getPath2(getCurrentLevelDir(), basename);
2621
2622   return filename;
2623 }
2624
2625 static int getFileTypeFromBasename(char *basename)
2626 {
2627   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2628
2629   static char *filename = NULL;
2630   struct stat file_status;
2631
2632   // ---------- try to determine file type from filename ----------
2633
2634   // check for typical filename of a Supaplex level package file
2635   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2636     return LEVEL_FILE_TYPE_SP;
2637
2638   // check for typical filename of a Diamond Caves II level package file
2639   if (strSuffixLower(basename, ".dc") ||
2640       strSuffixLower(basename, ".dc2"))
2641     return LEVEL_FILE_TYPE_DC;
2642
2643   // check for typical filename of a Sokoban level package file
2644   if (strSuffixLower(basename, ".xsb") &&
2645       strchr(basename, '%') == NULL)
2646     return LEVEL_FILE_TYPE_SB;
2647
2648   // check for typical filename of a Boulder Dash (GDash) level package file
2649   if (checkForPackageFromBasename_BD(basename))
2650     return LEVEL_FILE_TYPE_BD;
2651
2652   // ---------- try to determine file type from filesize ----------
2653
2654   checked_free(filename);
2655   filename = getPath2(getCurrentLevelDir(), basename);
2656
2657   if (stat(filename, &file_status) == 0)
2658   {
2659     // check for typical filesize of a Supaplex level package file
2660     if (file_status.st_size == 170496)
2661       return LEVEL_FILE_TYPE_SP;
2662   }
2663
2664   return LEVEL_FILE_TYPE_UNKNOWN;
2665 }
2666
2667 static int getFileTypeFromMagicBytes(char *filename, int type)
2668 {
2669   File *file;
2670
2671   if ((file = openFile(filename, MODE_READ)))
2672   {
2673     char chunk_name[CHUNK_ID_LEN + 1];
2674
2675     getFileChunkBE(file, chunk_name, NULL);
2676
2677     if (strEqual(chunk_name, "MMII") ||
2678         strEqual(chunk_name, "MIRR"))
2679       type = LEVEL_FILE_TYPE_MM;
2680
2681     closeFile(file);
2682   }
2683
2684   return type;
2685 }
2686
2687 static boolean checkForPackageFromBasename(char *basename)
2688 {
2689   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2690   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2691
2692   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2693 }
2694
2695 static char *getSingleLevelBasenameExt(int nr, char *extension)
2696 {
2697   static char basename[MAX_FILENAME_LEN];
2698
2699   if (nr < 0)
2700     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2701   else
2702     sprintf(basename, "%03d.%s", nr, extension);
2703
2704   return basename;
2705 }
2706
2707 static char *getSingleLevelBasename(int nr)
2708 {
2709   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2710 }
2711
2712 static char *getPackedLevelBasename(int type)
2713 {
2714   static char basename[MAX_FILENAME_LEN];
2715   char *directory = getCurrentLevelDir();
2716   Directory *dir;
2717   DirectoryEntry *dir_entry;
2718
2719   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2720
2721   if ((dir = openDirectory(directory)) == NULL)
2722   {
2723     Warn("cannot read current level directory '%s'", directory);
2724
2725     return basename;
2726   }
2727
2728   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2729   {
2730     char *entry_basename = dir_entry->basename;
2731     int entry_type = getFileTypeFromBasename(entry_basename);
2732
2733     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2734     {
2735       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2736           type == entry_type)
2737       {
2738         strcpy(basename, entry_basename);
2739
2740         break;
2741       }
2742     }
2743   }
2744
2745   closeDirectory(dir);
2746
2747   return basename;
2748 }
2749
2750 static char *getSingleLevelFilename(int nr)
2751 {
2752   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2753 }
2754
2755 #if ENABLE_UNUSED_CODE
2756 static char *getPackedLevelFilename(int type)
2757 {
2758   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2759 }
2760 #endif
2761
2762 char *getDefaultLevelFilename(int nr)
2763 {
2764   return getSingleLevelFilename(nr);
2765 }
2766
2767 #if ENABLE_UNUSED_CODE
2768 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2769                                                  int type)
2770 {
2771   lfi->type = type;
2772   lfi->packed = FALSE;
2773
2774   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2775   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2776 }
2777 #endif
2778
2779 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2780                                                  int type, char *format, ...)
2781 {
2782   static char basename[MAX_FILENAME_LEN];
2783   va_list ap;
2784
2785   va_start(ap, format);
2786   vsprintf(basename, format, ap);
2787   va_end(ap);
2788
2789   lfi->type = type;
2790   lfi->packed = FALSE;
2791
2792   setString(&lfi->basename, basename);
2793   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2794 }
2795
2796 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2797                                                  int type)
2798 {
2799   lfi->type = type;
2800   lfi->packed = TRUE;
2801
2802   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2803   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2804 }
2805
2806 static int getFiletypeFromID(char *filetype_id)
2807 {
2808   char *filetype_id_lower;
2809   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2810   int i;
2811
2812   if (filetype_id == NULL)
2813     return LEVEL_FILE_TYPE_UNKNOWN;
2814
2815   filetype_id_lower = getStringToLower(filetype_id);
2816
2817   for (i = 0; filetype_id_list[i].id != NULL; i++)
2818   {
2819     char *id_lower = getStringToLower(filetype_id_list[i].id);
2820     
2821     if (strEqual(filetype_id_lower, id_lower))
2822       filetype = filetype_id_list[i].filetype;
2823
2824     free(id_lower);
2825
2826     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2827       break;
2828   }
2829
2830   free(filetype_id_lower);
2831
2832   return filetype;
2833 }
2834
2835 char *getLocalLevelTemplateFilename(void)
2836 {
2837   return getDefaultLevelFilename(-1);
2838 }
2839
2840 char *getGlobalLevelTemplateFilename(void)
2841 {
2842   // global variable "leveldir_current" must be modified in the loop below
2843   LevelDirTree *leveldir_current_last = leveldir_current;
2844   char *filename = NULL;
2845
2846   // check for template level in path from current to topmost tree node
2847
2848   while (leveldir_current != NULL)
2849   {
2850     filename = getDefaultLevelFilename(-1);
2851
2852     if (fileExists(filename))
2853       break;
2854
2855     leveldir_current = leveldir_current->node_parent;
2856   }
2857
2858   // restore global variable "leveldir_current" modified in above loop
2859   leveldir_current = leveldir_current_last;
2860
2861   return filename;
2862 }
2863
2864 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2865 {
2866   int nr = lfi->nr;
2867
2868   // special case: level number is negative => check for level template file
2869   if (nr < 0)
2870   {
2871     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2872                                          getSingleLevelBasename(-1));
2873
2874     // replace local level template filename with global template filename
2875     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2876
2877     // no fallback if template file not existing
2878     return;
2879   }
2880
2881   // special case: check for file name/pattern specified in "levelinfo.conf"
2882   if (leveldir_current->level_filename != NULL)
2883   {
2884     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2885
2886     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2887                                          leveldir_current->level_filename, nr);
2888
2889     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2890
2891     if (fileExists(lfi->filename))
2892       return;
2893   }
2894   else if (leveldir_current->level_filetype != NULL)
2895   {
2896     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2897
2898     // check for specified native level file with standard file name
2899     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2900                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2901     if (fileExists(lfi->filename))
2902       return;
2903   }
2904
2905   // check for native Rocks'n'Diamonds level file
2906   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2907                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2908   if (fileExists(lfi->filename))
2909     return;
2910
2911   // check for native Boulder Dash level file
2912   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2913   if (fileExists(lfi->filename))
2914     return;
2915
2916   // check for Emerald Mine level file (V1)
2917   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2918                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2919   if (fileExists(lfi->filename))
2920     return;
2921   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2922                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2923   if (fileExists(lfi->filename))
2924     return;
2925
2926   // check for Emerald Mine level file (V2 to V5)
2927   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2928   if (fileExists(lfi->filename))
2929     return;
2930
2931   // check for Emerald Mine level file (V6 / single mode)
2932   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2933   if (fileExists(lfi->filename))
2934     return;
2935   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2936   if (fileExists(lfi->filename))
2937     return;
2938
2939   // check for Emerald Mine level file (V6 / teamwork mode)
2940   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2941   if (fileExists(lfi->filename))
2942     return;
2943   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2944   if (fileExists(lfi->filename))
2945     return;
2946
2947   // check for various packed level file formats
2948   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2949   if (fileExists(lfi->filename))
2950     return;
2951
2952   // no known level file found -- use default values (and fail later)
2953   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2954                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2955 }
2956
2957 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2958 {
2959   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2960     lfi->type = getFileTypeFromBasename(lfi->basename);
2961
2962   if (lfi->type == LEVEL_FILE_TYPE_RND)
2963     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2964 }
2965
2966 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2967 {
2968   // always start with reliable default values
2969   setFileInfoToDefaults(level_file_info);
2970
2971   level_file_info->nr = nr;     // set requested level number
2972
2973   determineLevelFileInfo_Filename(level_file_info);
2974   determineLevelFileInfo_Filetype(level_file_info);
2975 }
2976
2977 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2978                               struct LevelFileInfo *lfi_to)
2979 {
2980   lfi_to->nr     = lfi_from->nr;
2981   lfi_to->type   = lfi_from->type;
2982   lfi_to->packed = lfi_from->packed;
2983
2984   setString(&lfi_to->basename, lfi_from->basename);
2985   setString(&lfi_to->filename, lfi_from->filename);
2986 }
2987
2988 // ----------------------------------------------------------------------------
2989 // functions for loading R'n'D level
2990 // ----------------------------------------------------------------------------
2991
2992 int getMappedElement(int element)
2993 {
2994   // remap some (historic, now obsolete) elements
2995
2996   switch (element)
2997   {
2998     case EL_PLAYER_OBSOLETE:
2999       element = EL_PLAYER_1;
3000       break;
3001
3002     case EL_KEY_OBSOLETE:
3003       element = EL_KEY_1;
3004       break;
3005
3006     case EL_EM_KEY_1_FILE_OBSOLETE:
3007       element = EL_EM_KEY_1;
3008       break;
3009
3010     case EL_EM_KEY_2_FILE_OBSOLETE:
3011       element = EL_EM_KEY_2;
3012       break;
3013
3014     case EL_EM_KEY_3_FILE_OBSOLETE:
3015       element = EL_EM_KEY_3;
3016       break;
3017
3018     case EL_EM_KEY_4_FILE_OBSOLETE:
3019       element = EL_EM_KEY_4;
3020       break;
3021
3022     case EL_ENVELOPE_OBSOLETE:
3023       element = EL_ENVELOPE_1;
3024       break;
3025
3026     case EL_SP_EMPTY:
3027       element = EL_EMPTY;
3028       break;
3029
3030     default:
3031       if (element >= NUM_FILE_ELEMENTS)
3032       {
3033         Warn("invalid level element %d", element);
3034
3035         element = EL_UNKNOWN;
3036       }
3037       break;
3038   }
3039
3040   return element;
3041 }
3042
3043 static int getMappedElementByVersion(int element, int game_version)
3044 {
3045   // remap some elements due to certain game version
3046
3047   if (game_version <= VERSION_IDENT(2,2,0,0))
3048   {
3049     // map game font elements
3050     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3051                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3052                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3053                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3054   }
3055
3056   if (game_version < VERSION_IDENT(3,0,0,0))
3057   {
3058     // map Supaplex gravity tube elements
3059     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3060                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3061                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3062                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3063                element);
3064   }
3065
3066   return element;
3067 }
3068
3069 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3070 {
3071   level->file_version = getFileVersion(file);
3072   level->game_version = getFileVersion(file);
3073
3074   return chunk_size;
3075 }
3076
3077 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3078 {
3079   level->creation_date.year  = getFile16BitBE(file);
3080   level->creation_date.month = getFile8Bit(file);
3081   level->creation_date.day   = getFile8Bit(file);
3082
3083   level->creation_date.src   = DATE_SRC_LEVELFILE;
3084
3085   return chunk_size;
3086 }
3087
3088 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3089 {
3090   int initial_player_stepsize;
3091   int initial_player_gravity;
3092   int i, x, y;
3093
3094   level->fieldx = getFile8Bit(file);
3095   level->fieldy = getFile8Bit(file);
3096
3097   level->time           = getFile16BitBE(file);
3098   level->gems_needed    = getFile16BitBE(file);
3099
3100   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3101     level->name[i] = getFile8Bit(file);
3102   level->name[MAX_LEVEL_NAME_LEN] = 0;
3103
3104   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3105     level->score[i] = getFile8Bit(file);
3106
3107   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3108   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3109     for (y = 0; y < 3; y++)
3110       for (x = 0; x < 3; x++)
3111         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3112
3113   level->amoeba_speed           = getFile8Bit(file);
3114   level->time_magic_wall        = getFile8Bit(file);
3115   level->time_wheel             = getFile8Bit(file);
3116   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3117
3118   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3119                                    STEPSIZE_NORMAL);
3120
3121   for (i = 0; i < MAX_PLAYERS; i++)
3122     level->initial_player_stepsize[i] = initial_player_stepsize;
3123
3124   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3125
3126   for (i = 0; i < MAX_PLAYERS; i++)
3127     level->initial_player_gravity[i] = initial_player_gravity;
3128
3129   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3130   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3131
3132   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3133
3134   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3135   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3136   level->can_move_into_acid_bits = getFile32BitBE(file);
3137   level->dont_collide_with_bits = getFile8Bit(file);
3138
3139   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3140   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3141
3142   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3143   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3144   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3145
3146   level->game_engine_type       = getFile8Bit(file);
3147
3148   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3149
3150   return chunk_size;
3151 }
3152
3153 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3154 {
3155   int i;
3156
3157   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3158     level->name[i] = getFile8Bit(file);
3159   level->name[MAX_LEVEL_NAME_LEN] = 0;
3160
3161   return chunk_size;
3162 }
3163
3164 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3165 {
3166   int i;
3167
3168   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3169     level->author[i] = getFile8Bit(file);
3170   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3171
3172   return chunk_size;
3173 }
3174
3175 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3176 {
3177   int x, y;
3178   int chunk_size_expected = level->fieldx * level->fieldy;
3179
3180   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3181      stored with 16-bit encoding (and should be twice as big then).
3182      Even worse, playfield data was stored 16-bit when only yamyam content
3183      contained 16-bit elements and vice versa. */
3184
3185   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3186     chunk_size_expected *= 2;
3187
3188   if (chunk_size_expected != chunk_size)
3189   {
3190     ReadUnusedBytesFromFile(file, chunk_size);
3191     return chunk_size_expected;
3192   }
3193
3194   for (y = 0; y < level->fieldy; y++)
3195     for (x = 0; x < level->fieldx; x++)
3196       level->field[x][y] =
3197         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3198                          getFile8Bit(file));
3199   return chunk_size;
3200 }
3201
3202 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3203 {
3204   int i, x, y;
3205   int header_size = 4;
3206   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3207   int chunk_size_expected = header_size + content_size;
3208
3209   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3210      stored with 16-bit encoding (and should be twice as big then).
3211      Even worse, playfield data was stored 16-bit when only yamyam content
3212      contained 16-bit elements and vice versa. */
3213
3214   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3215     chunk_size_expected += content_size;
3216
3217   if (chunk_size_expected != chunk_size)
3218   {
3219     ReadUnusedBytesFromFile(file, chunk_size);
3220     return chunk_size_expected;
3221   }
3222
3223   getFile8Bit(file);
3224   level->num_yamyam_contents = getFile8Bit(file);
3225   getFile8Bit(file);
3226   getFile8Bit(file);
3227
3228   // correct invalid number of content fields -- should never happen
3229   if (level->num_yamyam_contents < 1 ||
3230       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3231     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3232
3233   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3234     for (y = 0; y < 3; y++)
3235       for (x = 0; x < 3; x++)
3236         level->yamyam_content[i].e[x][y] =
3237           getMappedElement(level->encoding_16bit_field ?
3238                            getFile16BitBE(file) : getFile8Bit(file));
3239   return chunk_size;
3240 }
3241
3242 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3243 {
3244   int i, x, y;
3245   int element;
3246   int num_contents;
3247   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3248
3249   element = getMappedElement(getFile16BitBE(file));
3250   num_contents = getFile8Bit(file);
3251
3252   getFile8Bit(file);    // content x size (unused)
3253   getFile8Bit(file);    // content y size (unused)
3254
3255   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3256
3257   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3258     for (y = 0; y < 3; y++)
3259       for (x = 0; x < 3; x++)
3260         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3261
3262   // correct invalid number of content fields -- should never happen
3263   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3264     num_contents = STD_ELEMENT_CONTENTS;
3265
3266   if (element == EL_YAMYAM)
3267   {
3268     level->num_yamyam_contents = num_contents;
3269
3270     for (i = 0; i < num_contents; i++)
3271       for (y = 0; y < 3; y++)
3272         for (x = 0; x < 3; x++)
3273           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3274   }
3275   else if (element == EL_BD_AMOEBA)
3276   {
3277     level->amoeba_content = content_array[0][0][0];
3278   }
3279   else
3280   {
3281     Warn("cannot load content for element '%d'", element);
3282   }
3283
3284   return chunk_size;
3285 }
3286
3287 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3288 {
3289   int i;
3290   int element;
3291   int envelope_nr;
3292   int envelope_len;
3293   int chunk_size_expected;
3294
3295   element = getMappedElement(getFile16BitBE(file));
3296   if (!IS_ENVELOPE(element))
3297     element = EL_ENVELOPE_1;
3298
3299   envelope_nr = element - EL_ENVELOPE_1;
3300
3301   envelope_len = getFile16BitBE(file);
3302
3303   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3304   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3305
3306   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3307
3308   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3309   if (chunk_size_expected != chunk_size)
3310   {
3311     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3312     return chunk_size_expected;
3313   }
3314
3315   for (i = 0; i < envelope_len; i++)
3316     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3317
3318   return chunk_size;
3319 }
3320
3321 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3322 {
3323   int num_changed_custom_elements = getFile16BitBE(file);
3324   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3325   int i;
3326
3327   if (chunk_size_expected != chunk_size)
3328   {
3329     ReadUnusedBytesFromFile(file, chunk_size - 2);
3330     return chunk_size_expected;
3331   }
3332
3333   for (i = 0; i < num_changed_custom_elements; i++)
3334   {
3335     int element = getMappedElement(getFile16BitBE(file));
3336     int properties = getFile32BitBE(file);
3337
3338     if (IS_CUSTOM_ELEMENT(element))
3339       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3340     else
3341       Warn("invalid custom element number %d", element);
3342
3343     // older game versions that wrote level files with CUS1 chunks used
3344     // different default push delay values (not yet stored in level file)
3345     element_info[element].push_delay_fixed = 2;
3346     element_info[element].push_delay_random = 8;
3347   }
3348
3349   level->file_has_custom_elements = TRUE;
3350
3351   return chunk_size;
3352 }
3353
3354 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3355 {
3356   int num_changed_custom_elements = getFile16BitBE(file);
3357   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3358   int i;
3359
3360   if (chunk_size_expected != chunk_size)
3361   {
3362     ReadUnusedBytesFromFile(file, chunk_size - 2);
3363     return chunk_size_expected;
3364   }
3365
3366   for (i = 0; i < num_changed_custom_elements; i++)
3367   {
3368     int element = getMappedElement(getFile16BitBE(file));
3369     int custom_target_element = getMappedElement(getFile16BitBE(file));
3370
3371     if (IS_CUSTOM_ELEMENT(element))
3372       element_info[element].change->target_element = custom_target_element;
3373     else
3374       Warn("invalid custom element number %d", element);
3375   }
3376
3377   level->file_has_custom_elements = TRUE;
3378
3379   return chunk_size;
3380 }
3381
3382 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3383 {
3384   int num_changed_custom_elements = getFile16BitBE(file);
3385   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3386   int i, j, x, y;
3387
3388   if (chunk_size_expected != chunk_size)
3389   {
3390     ReadUnusedBytesFromFile(file, chunk_size - 2);
3391     return chunk_size_expected;
3392   }
3393
3394   for (i = 0; i < num_changed_custom_elements; i++)
3395   {
3396     int element = getMappedElement(getFile16BitBE(file));
3397     struct ElementInfo *ei = &element_info[element];
3398     unsigned int event_bits;
3399
3400     if (!IS_CUSTOM_ELEMENT(element))
3401     {
3402       Warn("invalid custom element number %d", element);
3403
3404       element = EL_INTERNAL_DUMMY;
3405     }
3406
3407     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3408       ei->description[j] = getFile8Bit(file);
3409     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3410
3411     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3412
3413     // some free bytes for future properties and padding
3414     ReadUnusedBytesFromFile(file, 7);
3415
3416     ei->use_gfx_element = getFile8Bit(file);
3417     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3418
3419     ei->collect_score_initial = getFile8Bit(file);
3420     ei->collect_count_initial = getFile8Bit(file);
3421
3422     ei->push_delay_fixed = getFile16BitBE(file);
3423     ei->push_delay_random = getFile16BitBE(file);
3424     ei->move_delay_fixed = getFile16BitBE(file);
3425     ei->move_delay_random = getFile16BitBE(file);
3426
3427     ei->move_pattern = getFile16BitBE(file);
3428     ei->move_direction_initial = getFile8Bit(file);
3429     ei->move_stepsize = getFile8Bit(file);
3430
3431     for (y = 0; y < 3; y++)
3432       for (x = 0; x < 3; x++)
3433         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3434
3435     // bits 0 - 31 of "has_event[]"
3436     event_bits = getFile32BitBE(file);
3437     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3438       if (event_bits & (1u << j))
3439         ei->change->has_event[j] = TRUE;
3440
3441     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3442
3443     ei->change->delay_fixed = getFile16BitBE(file);
3444     ei->change->delay_random = getFile16BitBE(file);
3445     ei->change->delay_frames = getFile16BitBE(file);
3446
3447     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3448
3449     ei->change->explode = getFile8Bit(file);
3450     ei->change->use_target_content = getFile8Bit(file);
3451     ei->change->only_if_complete = getFile8Bit(file);
3452     ei->change->use_random_replace = getFile8Bit(file);
3453
3454     ei->change->random_percentage = getFile8Bit(file);
3455     ei->change->replace_when = getFile8Bit(file);
3456
3457     for (y = 0; y < 3; y++)
3458       for (x = 0; x < 3; x++)
3459         ei->change->target_content.e[x][y] =
3460           getMappedElement(getFile16BitBE(file));
3461
3462     ei->slippery_type = getFile8Bit(file);
3463
3464     // some free bytes for future properties and padding
3465     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3466
3467     // mark that this custom element has been modified
3468     ei->modified_settings = TRUE;
3469   }
3470
3471   level->file_has_custom_elements = TRUE;
3472
3473   return chunk_size;
3474 }
3475
3476 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3477 {
3478   struct ElementInfo *ei;
3479   int chunk_size_expected;
3480   int element;
3481   int i, j, x, y;
3482
3483   // ---------- custom element base property values (96 bytes) ----------------
3484
3485   element = getMappedElement(getFile16BitBE(file));
3486
3487   if (!IS_CUSTOM_ELEMENT(element))
3488   {
3489     Warn("invalid custom element number %d", element);
3490
3491     ReadUnusedBytesFromFile(file, chunk_size - 2);
3492
3493     return chunk_size;
3494   }
3495
3496   ei = &element_info[element];
3497
3498   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3499     ei->description[i] = getFile8Bit(file);
3500   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3501
3502   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3503
3504   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3505
3506   ei->num_change_pages = getFile8Bit(file);
3507
3508   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3509   if (chunk_size_expected != chunk_size)
3510   {
3511     ReadUnusedBytesFromFile(file, chunk_size - 43);
3512     return chunk_size_expected;
3513   }
3514
3515   ei->ce_value_fixed_initial = getFile16BitBE(file);
3516   ei->ce_value_random_initial = getFile16BitBE(file);
3517   ei->use_last_ce_value = getFile8Bit(file);
3518
3519   ei->use_gfx_element = getFile8Bit(file);
3520   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3521
3522   ei->collect_score_initial = getFile8Bit(file);
3523   ei->collect_count_initial = getFile8Bit(file);
3524
3525   ei->drop_delay_fixed = getFile8Bit(file);
3526   ei->push_delay_fixed = getFile8Bit(file);
3527   ei->drop_delay_random = getFile8Bit(file);
3528   ei->push_delay_random = getFile8Bit(file);
3529   ei->move_delay_fixed = getFile16BitBE(file);
3530   ei->move_delay_random = getFile16BitBE(file);
3531
3532   // bits 0 - 15 of "move_pattern" ...
3533   ei->move_pattern = getFile16BitBE(file);
3534   ei->move_direction_initial = getFile8Bit(file);
3535   ei->move_stepsize = getFile8Bit(file);
3536
3537   ei->slippery_type = getFile8Bit(file);
3538
3539   for (y = 0; y < 3; y++)
3540     for (x = 0; x < 3; x++)
3541       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3542
3543   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3544   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3545   ei->move_leave_type = getFile8Bit(file);
3546
3547   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3548   ei->move_pattern |= (getFile16BitBE(file) << 16);
3549
3550   ei->access_direction = getFile8Bit(file);
3551
3552   ei->explosion_delay = getFile8Bit(file);
3553   ei->ignition_delay = getFile8Bit(file);
3554   ei->explosion_type = getFile8Bit(file);
3555
3556   // some free bytes for future custom property values and padding
3557   ReadUnusedBytesFromFile(file, 1);
3558
3559   // ---------- change page property values (48 bytes) ------------------------
3560
3561   setElementChangePages(ei, ei->num_change_pages);
3562
3563   for (i = 0; i < ei->num_change_pages; i++)
3564   {
3565     struct ElementChangeInfo *change = &ei->change_page[i];
3566     unsigned int event_bits;
3567
3568     // always start with reliable default values
3569     setElementChangeInfoToDefaults(change);
3570
3571     // bits 0 - 31 of "has_event[]" ...
3572     event_bits = getFile32BitBE(file);
3573     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3574       if (event_bits & (1u << j))
3575         change->has_event[j] = TRUE;
3576
3577     change->target_element = getMappedElement(getFile16BitBE(file));
3578
3579     change->delay_fixed = getFile16BitBE(file);
3580     change->delay_random = getFile16BitBE(file);
3581     change->delay_frames = getFile16BitBE(file);
3582
3583     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3584
3585     change->explode = getFile8Bit(file);
3586     change->use_target_content = getFile8Bit(file);
3587     change->only_if_complete = getFile8Bit(file);
3588     change->use_random_replace = getFile8Bit(file);
3589
3590     change->random_percentage = getFile8Bit(file);
3591     change->replace_when = getFile8Bit(file);
3592
3593     for (y = 0; y < 3; y++)
3594       for (x = 0; x < 3; x++)
3595         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3596
3597     change->can_change = getFile8Bit(file);
3598
3599     change->trigger_side = getFile8Bit(file);
3600
3601     change->trigger_player = getFile8Bit(file);
3602     change->trigger_page = getFile8Bit(file);
3603
3604     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3605                             CH_PAGE_ANY : (1 << change->trigger_page));
3606
3607     change->has_action = getFile8Bit(file);
3608     change->action_type = getFile8Bit(file);
3609     change->action_mode = getFile8Bit(file);
3610     change->action_arg = getFile16BitBE(file);
3611
3612     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3613     event_bits = getFile8Bit(file);
3614     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3615       if (event_bits & (1u << (j - 32)))
3616         change->has_event[j] = TRUE;
3617   }
3618
3619   // mark this custom element as modified
3620   ei->modified_settings = TRUE;
3621
3622   level->file_has_custom_elements = TRUE;
3623
3624   return chunk_size;
3625 }
3626
3627 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3628 {
3629   struct ElementInfo *ei;
3630   struct ElementGroupInfo *group;
3631   int element;
3632   int i;
3633
3634   element = getMappedElement(getFile16BitBE(file));
3635
3636   if (!IS_GROUP_ELEMENT(element))
3637   {
3638     Warn("invalid group element number %d", element);
3639
3640     ReadUnusedBytesFromFile(file, chunk_size - 2);
3641
3642     return chunk_size;
3643   }
3644
3645   ei = &element_info[element];
3646
3647   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3648     ei->description[i] = getFile8Bit(file);
3649   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3650
3651   group = element_info[element].group;
3652
3653   group->num_elements = getFile8Bit(file);
3654
3655   ei->use_gfx_element = getFile8Bit(file);
3656   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3657
3658   group->choice_mode = getFile8Bit(file);
3659
3660   // some free bytes for future values and padding
3661   ReadUnusedBytesFromFile(file, 3);
3662
3663   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3664     group->element[i] = getMappedElement(getFile16BitBE(file));
3665
3666   // mark this group element as modified
3667   element_info[element].modified_settings = TRUE;
3668
3669   level->file_has_custom_elements = TRUE;
3670
3671   return chunk_size;
3672 }
3673
3674 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3675                                 int element, int real_element)
3676 {
3677   int micro_chunk_size = 0;
3678   int conf_type = getFile8Bit(file);
3679   int byte_mask = conf_type & CONF_MASK_BYTES;
3680   boolean element_found = FALSE;
3681   int i;
3682
3683   micro_chunk_size += 1;
3684
3685   if (byte_mask == CONF_MASK_MULTI_BYTES)
3686   {
3687     int num_bytes = getFile16BitBE(file);
3688     byte *buffer = checked_malloc(num_bytes);
3689
3690     ReadBytesFromFile(file, buffer, num_bytes);
3691
3692     for (i = 0; conf[i].data_type != -1; i++)
3693     {
3694       if (conf[i].element == element &&
3695           conf[i].conf_type == conf_type)
3696       {
3697         int data_type = conf[i].data_type;
3698         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3699         int max_num_entities = conf[i].max_num_entities;
3700
3701         if (num_entities > max_num_entities)
3702         {
3703           Warn("truncating number of entities for element %d from %d to %d",
3704                element, num_entities, max_num_entities);
3705
3706           num_entities = max_num_entities;
3707         }
3708
3709         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3710                                   data_type == TYPE_CONTENT_LIST))
3711         {
3712           // for element and content lists, zero entities are not allowed
3713           Warn("found empty list of entities for element %d", element);
3714
3715           // do not set "num_entities" here to prevent reading behind buffer
3716
3717           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3718         }
3719         else
3720         {
3721           *(int *)(conf[i].num_entities) = num_entities;
3722         }
3723
3724         element_found = TRUE;
3725
3726         if (data_type == TYPE_STRING)
3727         {
3728           char *string = (char *)(conf[i].value);
3729           int j;
3730
3731           for (j = 0; j < max_num_entities; j++)
3732             string[j] = (j < num_entities ? buffer[j] : '\0');
3733         }
3734         else if (data_type == TYPE_ELEMENT_LIST)
3735         {
3736           int *element_array = (int *)(conf[i].value);
3737           int j;
3738
3739           for (j = 0; j < num_entities; j++)
3740             element_array[j] =
3741               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3742         }
3743         else if (data_type == TYPE_CONTENT_LIST)
3744         {
3745           struct Content *content= (struct Content *)(conf[i].value);
3746           int c, x, y;
3747
3748           for (c = 0; c < num_entities; c++)
3749             for (y = 0; y < 3; y++)
3750               for (x = 0; x < 3; x++)
3751                 content[c].e[x][y] =
3752                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3753         }
3754         else
3755           element_found = FALSE;
3756
3757         break;
3758       }
3759     }
3760
3761     checked_free(buffer);
3762
3763     micro_chunk_size += 2 + num_bytes;
3764   }
3765   else          // constant size configuration data (1, 2 or 4 bytes)
3766   {
3767     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3768                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3769                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3770
3771     for (i = 0; conf[i].data_type != -1; i++)
3772     {
3773       if (conf[i].element == element &&
3774           conf[i].conf_type == conf_type)
3775       {
3776         int data_type = conf[i].data_type;
3777
3778         if (data_type == TYPE_ELEMENT)
3779           value = getMappedElement(value);
3780
3781         if (data_type == TYPE_BOOLEAN)
3782           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3783         else
3784           *(int *)    (conf[i].value) = value;
3785
3786         element_found = TRUE;
3787
3788         break;
3789       }
3790     }
3791
3792     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3793   }
3794
3795   if (!element_found)
3796   {
3797     char *error_conf_chunk_bytes =
3798       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3799        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3800        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3801     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3802     int error_element = real_element;
3803
3804     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3805          error_conf_chunk_bytes, error_conf_chunk_token,
3806          error_element, EL_NAME(error_element));
3807   }
3808
3809   return micro_chunk_size;
3810 }
3811
3812 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3813 {
3814   int real_chunk_size = 0;
3815
3816   li = *level;          // copy level data into temporary buffer
3817
3818   while (!checkEndOfFile(file))
3819   {
3820     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3821
3822     if (real_chunk_size >= chunk_size)
3823       break;
3824   }
3825
3826   *level = li;          // copy temporary buffer back to level data
3827
3828   return real_chunk_size;
3829 }
3830
3831 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3832 {
3833   int real_chunk_size = 0;
3834
3835   li = *level;          // copy level data into temporary buffer
3836
3837   while (!checkEndOfFile(file))
3838   {
3839     int element = getMappedElement(getFile16BitBE(file));
3840
3841     real_chunk_size += 2;
3842     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3843                                             element, element);
3844     if (real_chunk_size >= chunk_size)
3845       break;
3846   }
3847
3848   *level = li;          // copy temporary buffer back to level data
3849
3850   return real_chunk_size;
3851 }
3852
3853 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3854 {
3855   int real_chunk_size = 0;
3856
3857   li = *level;          // copy level data into temporary buffer
3858
3859   while (!checkEndOfFile(file))
3860   {
3861     int element = getMappedElement(getFile16BitBE(file));
3862
3863     real_chunk_size += 2;
3864     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3865                                             element, element);
3866     if (real_chunk_size >= chunk_size)
3867       break;
3868   }
3869
3870   *level = li;          // copy temporary buffer back to level data
3871
3872   return real_chunk_size;
3873 }
3874
3875 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3876 {
3877   int element = getMappedElement(getFile16BitBE(file));
3878   int envelope_nr = element - EL_ENVELOPE_1;
3879   int real_chunk_size = 2;
3880
3881   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3882
3883   while (!checkEndOfFile(file))
3884   {
3885     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3886                                             -1, element);
3887
3888     if (real_chunk_size >= chunk_size)
3889       break;
3890   }
3891
3892   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3893
3894   return real_chunk_size;
3895 }
3896
3897 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3898 {
3899   int element = getMappedElement(getFile16BitBE(file));
3900   int real_chunk_size = 2;
3901   struct ElementInfo *ei = &element_info[element];
3902   int i;
3903
3904   xx_ei = *ei;          // copy element data into temporary buffer
3905
3906   xx_ei.num_change_pages = -1;
3907
3908   while (!checkEndOfFile(file))
3909   {
3910     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3911                                             -1, element);
3912     if (xx_ei.num_change_pages != -1)
3913       break;
3914
3915     if (real_chunk_size >= chunk_size)
3916       break;
3917   }
3918
3919   *ei = xx_ei;
3920
3921   if (ei->num_change_pages == -1)
3922   {
3923     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3924          EL_NAME(element));
3925
3926     ei->num_change_pages = 1;
3927
3928     setElementChangePages(ei, 1);
3929     setElementChangeInfoToDefaults(ei->change);
3930
3931     return real_chunk_size;
3932   }
3933
3934   // initialize number of change pages stored for this custom element
3935   setElementChangePages(ei, ei->num_change_pages);
3936   for (i = 0; i < ei->num_change_pages; i++)
3937     setElementChangeInfoToDefaults(&ei->change_page[i]);
3938
3939   // start with reading properties for the first change page
3940   xx_current_change_page = 0;
3941
3942   while (!checkEndOfFile(file))
3943   {
3944     // level file might contain invalid change page number
3945     if (xx_current_change_page >= ei->num_change_pages)
3946       break;
3947
3948     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3949
3950     xx_change = *change;        // copy change data into temporary buffer
3951
3952     resetEventBits();           // reset bits; change page might have changed
3953
3954     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3955                                             -1, element);
3956
3957     *change = xx_change;
3958
3959     setEventFlagsFromEventBits(change);
3960
3961     if (real_chunk_size >= chunk_size)
3962       break;
3963   }
3964
3965   level->file_has_custom_elements = TRUE;
3966
3967   return real_chunk_size;
3968 }
3969
3970 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3971 {
3972   int element = getMappedElement(getFile16BitBE(file));
3973   int real_chunk_size = 2;
3974   struct ElementInfo *ei = &element_info[element];
3975   struct ElementGroupInfo *group = ei->group;
3976
3977   if (group == NULL)
3978     return -1;
3979
3980   xx_ei = *ei;          // copy element data into temporary buffer
3981   xx_group = *group;    // copy group data into temporary buffer
3982
3983   while (!checkEndOfFile(file))
3984   {
3985     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3986                                             -1, element);
3987
3988     if (real_chunk_size >= chunk_size)
3989       break;
3990   }
3991
3992   *ei = xx_ei;
3993   *group = xx_group;
3994
3995   level->file_has_custom_elements = TRUE;
3996
3997   return real_chunk_size;
3998 }
3999
4000 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4001 {
4002   int element = getMappedElement(getFile16BitBE(file));
4003   int real_chunk_size = 2;
4004   struct ElementInfo *ei = &element_info[element];
4005
4006   xx_ei = *ei;          // copy element data into temporary buffer
4007
4008   while (!checkEndOfFile(file))
4009   {
4010     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4011                                             -1, element);
4012
4013     if (real_chunk_size >= chunk_size)
4014       break;
4015   }
4016
4017   *ei = xx_ei;
4018
4019   level->file_has_custom_elements = TRUE;
4020
4021   return real_chunk_size;
4022 }
4023
4024 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4025                                       struct LevelFileInfo *level_file_info,
4026                                       boolean level_info_only)
4027 {
4028   char *filename = level_file_info->filename;
4029   char cookie[MAX_LINE_LEN];
4030   char chunk_name[CHUNK_ID_LEN + 1];
4031   int chunk_size;
4032   File *file;
4033
4034   if (!(file = openFile(filename, MODE_READ)))
4035   {
4036     level->no_valid_file = TRUE;
4037     level->no_level_file = TRUE;
4038
4039     if (level_info_only)
4040       return;
4041
4042     Warn("cannot read level '%s' -- using empty level", filename);
4043
4044     if (!setup.editor.use_template_for_new_levels)
4045       return;
4046
4047     // if level file not found, try to initialize level data from template
4048     filename = getGlobalLevelTemplateFilename();
4049
4050     if (!(file = openFile(filename, MODE_READ)))
4051       return;
4052
4053     // default: for empty levels, use level template for custom elements
4054     level->use_custom_template = TRUE;
4055
4056     level->no_valid_file = FALSE;
4057   }
4058
4059   getFileChunkBE(file, chunk_name, NULL);
4060   if (strEqual(chunk_name, "RND1"))
4061   {
4062     getFile32BitBE(file);               // not used
4063
4064     getFileChunkBE(file, chunk_name, NULL);
4065     if (!strEqual(chunk_name, "CAVE"))
4066     {
4067       level->no_valid_file = TRUE;
4068
4069       Warn("unknown format of level file '%s'", filename);
4070
4071       closeFile(file);
4072
4073       return;
4074     }
4075   }
4076   else  // check for pre-2.0 file format with cookie string
4077   {
4078     strcpy(cookie, chunk_name);
4079     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4080       cookie[4] = '\0';
4081     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4082       cookie[strlen(cookie) - 1] = '\0';
4083
4084     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4085     {
4086       level->no_valid_file = TRUE;
4087
4088       Warn("unknown format of level file '%s'", filename);
4089
4090       closeFile(file);
4091
4092       return;
4093     }
4094
4095     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4096     {
4097       level->no_valid_file = TRUE;
4098
4099       Warn("unsupported version of level file '%s'", filename);
4100
4101       closeFile(file);
4102
4103       return;
4104     }
4105
4106     // pre-2.0 level files have no game version, so use file version here
4107     level->game_version = level->file_version;
4108   }
4109
4110   if (level->file_version < FILE_VERSION_1_2)
4111   {
4112     // level files from versions before 1.2.0 without chunk structure
4113     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4114     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4115   }
4116   else
4117   {
4118     static struct
4119     {
4120       char *name;
4121       int size;
4122       int (*loader)(File *, int, struct LevelInfo *);
4123     }
4124     chunk_info[] =
4125     {
4126       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4127       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4128       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4129       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4130       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4131       { "INFO", -1,                     LoadLevel_INFO },
4132       { "BODY", -1,                     LoadLevel_BODY },
4133       { "CONT", -1,                     LoadLevel_CONT },
4134       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4135       { "CNT3", -1,                     LoadLevel_CNT3 },
4136       { "CUS1", -1,                     LoadLevel_CUS1 },
4137       { "CUS2", -1,                     LoadLevel_CUS2 },
4138       { "CUS3", -1,                     LoadLevel_CUS3 },
4139       { "CUS4", -1,                     LoadLevel_CUS4 },
4140       { "GRP1", -1,                     LoadLevel_GRP1 },
4141       { "CONF", -1,                     LoadLevel_CONF },
4142       { "ELEM", -1,                     LoadLevel_ELEM },
4143       { "NOTE", -1,                     LoadLevel_NOTE },
4144       { "CUSX", -1,                     LoadLevel_CUSX },
4145       { "GRPX", -1,                     LoadLevel_GRPX },
4146       { "EMPX", -1,                     LoadLevel_EMPX },
4147
4148       {  NULL,  0,                      NULL }
4149     };
4150
4151     while (getFileChunkBE(file, chunk_name, &chunk_size))
4152     {
4153       int i = 0;
4154
4155       while (chunk_info[i].name != NULL &&
4156              !strEqual(chunk_name, chunk_info[i].name))
4157         i++;
4158
4159       if (chunk_info[i].name == NULL)
4160       {
4161         Warn("unknown chunk '%s' in level file '%s'",
4162              chunk_name, filename);
4163
4164         ReadUnusedBytesFromFile(file, chunk_size);
4165       }
4166       else if (chunk_info[i].size != -1 &&
4167                chunk_info[i].size != chunk_size)
4168       {
4169         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4170              chunk_size, chunk_name, filename);
4171
4172         ReadUnusedBytesFromFile(file, chunk_size);
4173       }
4174       else
4175       {
4176         // call function to load this level chunk
4177         int chunk_size_expected =
4178           (chunk_info[i].loader)(file, chunk_size, level);
4179
4180         if (chunk_size_expected < 0)
4181         {
4182           Warn("error reading chunk '%s' in level file '%s'",
4183                chunk_name, filename);
4184
4185           break;
4186         }
4187
4188         // the size of some chunks cannot be checked before reading other
4189         // chunks first (like "HEAD" and "BODY") that contain some header
4190         // information, so check them here
4191         if (chunk_size_expected != chunk_size)
4192         {
4193           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4194                chunk_size, chunk_name, filename);
4195
4196           break;
4197         }
4198       }
4199     }
4200   }
4201
4202   closeFile(file);
4203 }
4204
4205
4206 // ----------------------------------------------------------------------------
4207 // functions for loading BD level
4208 // ----------------------------------------------------------------------------
4209
4210 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4211 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4212
4213 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4214 {
4215   struct LevelInfo_BD *level_bd = level->native_bd_level;
4216   GdCave *cave = NULL;  // will be changed below
4217   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4218   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4219   int x, y;
4220
4221   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4222
4223   // cave and map newly allocated when set to defaults above
4224   cave = level_bd->cave;
4225
4226   // level type
4227   cave->intermission                    = level->bd_intermission;
4228
4229   // level settings
4230   cave->level_time[0]                   = level->time;
4231   cave->level_diamonds[0]               = level->gems_needed;
4232
4233   // game timing
4234   cave->scheduling                      = level->bd_scheduling_type;
4235   cave->pal_timing                      = level->bd_pal_timing;
4236   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4237   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4238   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4239   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4240
4241   // scores
4242   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4243   cave->diamond_value                   = level->score[SC_EMERALD];
4244   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4245
4246   // compatibility settings
4247   cave->lineshift                       = level->bd_line_shifting_borders;
4248   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4249   cave->short_explosions                = level->bd_short_explosions;
4250
4251   // player properties
4252   cave->diagonal_movements              = level->bd_diagonal_movements;
4253   cave->active_is_first_found           = level->bd_topmost_player_active;
4254   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4255   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4256   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4257   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4258
4259   // element properties
4260   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4261   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4262   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4263   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4264   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4265   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4266   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4267   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4268   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4269
4270   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4271   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4272   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4273   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4274   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4275   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4276   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4277
4278   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4279   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4280   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4281   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4282   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4283   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4284   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4285   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4286   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4287   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4288   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4289
4290   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4291   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4292   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4293   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4294   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4295   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4296
4297   cave->slime_predictable               = level->bd_slime_is_predictable;
4298   cave->slime_correct_random            = level->bd_slime_correct_random;
4299   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4300   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4301   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4302   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4303   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4304   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4305   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4306   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4307   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4308   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4309
4310   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4311   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4312   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4313
4314   cave->biter_delay_frame               = level->bd_biter_move_delay;
4315   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4316
4317   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4318
4319   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4320
4321   cave->replicators_active              = level->bd_replicators_active;
4322   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4323
4324   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4325   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4326
4327   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4328
4329   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4330
4331   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4332   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4333   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4334
4335   cave->infinite_rockets                = level->bd_infinite_rockets;
4336
4337   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4338   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4339
4340   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4341   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4342
4343   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4344   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4345   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4346
4347   cave->gravity                         = level->bd_gravity_direction;
4348   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4349   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4350   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4351
4352   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4353   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4354   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4355   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4356
4357   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4358   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4359   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4360   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4361   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4362   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4363
4364   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4365   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4366   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4367   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4368
4369   // level name
4370   strncpy(cave->name, level->name, sizeof(GdString));
4371   cave->name[sizeof(GdString) - 1] = '\0';
4372
4373   // playfield elements
4374   for (x = 0; x < cave->w; x++)
4375     for (y = 0; y < cave->h; y++)
4376       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4377 }
4378
4379 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4380 {
4381   struct LevelInfo_BD *level_bd = level->native_bd_level;
4382   GdCave *cave = level_bd->cave;
4383   int bd_level_nr = level_bd->level_nr;
4384   int x, y;
4385
4386   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4387   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4388
4389   // level type
4390   level->bd_intermission                = cave->intermission;
4391
4392   // level settings
4393   level->time                           = cave->level_time[bd_level_nr];
4394   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4395
4396   // game timing
4397   level->bd_scheduling_type             = cave->scheduling;
4398   level->bd_pal_timing                  = cave->pal_timing;
4399   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4400   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4401   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4402   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4403
4404   // scores
4405   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4406   level->score[SC_EMERALD]              = cave->diamond_value;
4407   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4408
4409   // compatibility settings
4410   level->bd_line_shifting_borders       = cave->lineshift;
4411   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4412   level->bd_short_explosions            = cave->short_explosions;
4413
4414   // player properties
4415   level->bd_diagonal_movements          = cave->diagonal_movements;
4416   level->bd_topmost_player_active       = cave->active_is_first_found;
4417   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4418   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4419   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4420   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4421
4422   // element properties
4423   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4424   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4425   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4426   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4427   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4428   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4429   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4430   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4431   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4432
4433   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4434   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4435   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4436   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4437   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4438   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4439   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4440
4441   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4442   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4443   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4444   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4445   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4446   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4447   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4448   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4449   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4450   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4451   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4452
4453   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4454   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4455   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4456   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4457   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4458   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4459
4460   level->bd_slime_is_predictable        = cave->slime_predictable;
4461   level->bd_slime_correct_random        = cave->slime_correct_random;
4462   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4463   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4464   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4465   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4466   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4467   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4468   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4469   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4470   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4471   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4472
4473   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4474   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4475   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4476
4477   level->bd_biter_move_delay            = cave->biter_delay_frame;
4478   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4479
4480   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4481
4482   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4483
4484   level->bd_replicators_active          = cave->replicators_active;
4485   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4486
4487   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4488   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4489
4490   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4491
4492   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4493
4494   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4495   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4496   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4497
4498   level->bd_infinite_rockets            = cave->infinite_rockets;
4499
4500   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4501   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4502
4503   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4504   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4505
4506   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4507   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4508   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4509
4510   level->bd_gravity_direction           = cave->gravity;
4511   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4512   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4513   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4514
4515   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4516   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4517   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4518   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4519
4520   level->bd_firefly_explodes_to         = CAVE_TO_LEVEL(cave->firefly_explode_to);
4521   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4522   level->bd_butterfly_explodes_to       = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4523   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4524   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4525   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4526
4527   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4528   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4529   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4530   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4531
4532   // level name
4533   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4534
4535   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4536   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4537
4538   // playfield elements
4539   for (x = 0; x < level->fieldx; x++)
4540     for (y = 0; y < level->fieldy; y++)
4541       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4542
4543   checked_free(cave_name);
4544 }
4545
4546 static void setTapeInfoToDefaults(void);
4547
4548 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4549 {
4550   struct LevelInfo_BD *level_bd = level->native_bd_level;
4551   GdCave *cave = level_bd->cave;
4552   GdReplay *replay = level_bd->replay;
4553   int i;
4554
4555   if (replay == NULL)
4556     return;
4557
4558   // always start with reliable default values
4559   setTapeInfoToDefaults();
4560
4561   tape.level_nr = level_nr;             // (currently not used)
4562   tape.random_seed = replay->seed;
4563
4564   TapeSetDateFromIsoDateString(replay->date);
4565
4566   tape.counter = 0;
4567   tape.pos[tape.counter].delay = 0;
4568
4569   tape.bd_replay = TRUE;
4570
4571   // all time calculations only used to display approximate tape time
4572   int cave_speed = cave->speed;
4573   int milliseconds_game = 0;
4574   int milliseconds_elapsed = 20;
4575
4576   for (i = 0; i < replay->movements->len; i++)
4577   {
4578     int replay_action = replay->movements->data[i];
4579     int tape_action = map_action_BD_to_RND(replay_action);
4580     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4581     boolean success = 0;
4582
4583     while (1)
4584     {
4585       success = TapeAddAction(action);
4586
4587       milliseconds_game += milliseconds_elapsed;
4588
4589       if (milliseconds_game >= cave_speed)
4590       {
4591         milliseconds_game -= cave_speed;
4592
4593         break;
4594       }
4595     }
4596
4597     tape.counter++;
4598     tape.pos[tape.counter].delay = 0;
4599     tape.pos[tape.counter].action[0] = 0;
4600
4601     if (!success)
4602     {
4603       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4604
4605       break;
4606     }
4607   }
4608
4609   TapeHaltRecording();
4610 }
4611
4612
4613 // ----------------------------------------------------------------------------
4614 // functions for loading EM level
4615 // ----------------------------------------------------------------------------
4616
4617 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4618 {
4619   static int ball_xy[8][2] =
4620   {
4621     { 0, 0 },
4622     { 1, 0 },
4623     { 2, 0 },
4624     { 0, 1 },
4625     { 2, 1 },
4626     { 0, 2 },
4627     { 1, 2 },
4628     { 2, 2 },
4629   };
4630   struct LevelInfo_EM *level_em = level->native_em_level;
4631   struct CAVE *cav = level_em->cav;
4632   int i, j, x, y;
4633
4634   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4635   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4636
4637   cav->time_seconds     = level->time;
4638   cav->gems_needed      = level->gems_needed;
4639
4640   cav->emerald_score    = level->score[SC_EMERALD];
4641   cav->diamond_score    = level->score[SC_DIAMOND];
4642   cav->alien_score      = level->score[SC_ROBOT];
4643   cav->tank_score       = level->score[SC_SPACESHIP];
4644   cav->bug_score        = level->score[SC_BUG];
4645   cav->eater_score      = level->score[SC_YAMYAM];
4646   cav->nut_score        = level->score[SC_NUT];
4647   cav->dynamite_score   = level->score[SC_DYNAMITE];
4648   cav->key_score        = level->score[SC_KEY];
4649   cav->exit_score       = level->score[SC_TIME_BONUS];
4650
4651   cav->num_eater_arrays = level->num_yamyam_contents;
4652
4653   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4654     for (y = 0; y < 3; y++)
4655       for (x = 0; x < 3; x++)
4656         cav->eater_array[i][y * 3 + x] =
4657           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4658
4659   cav->amoeba_time              = level->amoeba_speed;
4660   cav->wonderwall_time          = level->time_magic_wall;
4661   cav->wheel_time               = level->time_wheel;
4662
4663   cav->android_move_time        = level->android_move_time;
4664   cav->android_clone_time       = level->android_clone_time;
4665   cav->ball_random              = level->ball_random;
4666   cav->ball_active              = level->ball_active_initial;
4667   cav->ball_time                = level->ball_time;
4668   cav->num_ball_arrays          = level->num_ball_contents;
4669
4670   cav->lenses_score             = level->lenses_score;
4671   cav->magnify_score            = level->magnify_score;
4672   cav->slurp_score              = level->slurp_score;
4673
4674   cav->lenses_time              = level->lenses_time;
4675   cav->magnify_time             = level->magnify_time;
4676
4677   cav->wind_time = 9999;
4678   cav->wind_direction =
4679     map_direction_RND_to_EM(level->wind_direction_initial);
4680
4681   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4682     for (j = 0; j < 8; j++)
4683       cav->ball_array[i][j] =
4684         map_element_RND_to_EM_cave(level->ball_content[i].
4685                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4686
4687   map_android_clone_elements_RND_to_EM(level);
4688
4689   // first fill the complete playfield with the empty space element
4690   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4691     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4692       cav->cave[x][y] = Cblank;
4693
4694   // then copy the real level contents from level file into the playfield
4695   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4696   {
4697     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4698
4699     if (level->field[x][y] == EL_AMOEBA_DEAD)
4700       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4701
4702     cav->cave[x][y] = new_element;
4703   }
4704
4705   for (i = 0; i < MAX_PLAYERS; i++)
4706   {
4707     cav->player_x[i] = -1;
4708     cav->player_y[i] = -1;
4709   }
4710
4711   // initialize player positions and delete players from the playfield
4712   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4713   {
4714     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4715     {
4716       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4717
4718       cav->player_x[player_nr] = x;
4719       cav->player_y[player_nr] = y;
4720
4721       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4722     }
4723   }
4724 }
4725
4726 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4727 {
4728   static int ball_xy[8][2] =
4729   {
4730     { 0, 0 },
4731     { 1, 0 },
4732     { 2, 0 },
4733     { 0, 1 },
4734     { 2, 1 },
4735     { 0, 2 },
4736     { 1, 2 },
4737     { 2, 2 },
4738   };
4739   struct LevelInfo_EM *level_em = level->native_em_level;
4740   struct CAVE *cav = level_em->cav;
4741   int i, j, x, y;
4742
4743   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4744   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4745
4746   level->time        = cav->time_seconds;
4747   level->gems_needed = cav->gems_needed;
4748
4749   sprintf(level->name, "Level %d", level->file_info.nr);
4750
4751   level->score[SC_EMERALD]      = cav->emerald_score;
4752   level->score[SC_DIAMOND]      = cav->diamond_score;
4753   level->score[SC_ROBOT]        = cav->alien_score;
4754   level->score[SC_SPACESHIP]    = cav->tank_score;
4755   level->score[SC_BUG]          = cav->bug_score;
4756   level->score[SC_YAMYAM]       = cav->eater_score;
4757   level->score[SC_NUT]          = cav->nut_score;
4758   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4759   level->score[SC_KEY]          = cav->key_score;
4760   level->score[SC_TIME_BONUS]   = cav->exit_score;
4761
4762   level->num_yamyam_contents    = cav->num_eater_arrays;
4763
4764   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4765     for (y = 0; y < 3; y++)
4766       for (x = 0; x < 3; x++)
4767         level->yamyam_content[i].e[x][y] =
4768           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4769
4770   level->amoeba_speed           = cav->amoeba_time;
4771   level->time_magic_wall        = cav->wonderwall_time;
4772   level->time_wheel             = cav->wheel_time;
4773
4774   level->android_move_time      = cav->android_move_time;
4775   level->android_clone_time     = cav->android_clone_time;
4776   level->ball_random            = cav->ball_random;
4777   level->ball_active_initial    = cav->ball_active;
4778   level->ball_time              = cav->ball_time;
4779   level->num_ball_contents      = cav->num_ball_arrays;
4780
4781   level->lenses_score           = cav->lenses_score;
4782   level->magnify_score          = cav->magnify_score;
4783   level->slurp_score            = cav->slurp_score;
4784
4785   level->lenses_time            = cav->lenses_time;
4786   level->magnify_time           = cav->magnify_time;
4787
4788   level->wind_direction_initial =
4789     map_direction_EM_to_RND(cav->wind_direction);
4790
4791   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4792     for (j = 0; j < 8; j++)
4793       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4794         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4795
4796   map_android_clone_elements_EM_to_RND(level);
4797
4798   // convert the playfield (some elements need special treatment)
4799   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4800   {
4801     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4802
4803     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4804       new_element = EL_AMOEBA_DEAD;
4805
4806     level->field[x][y] = new_element;
4807   }
4808
4809   for (i = 0; i < MAX_PLAYERS; i++)
4810   {
4811     // in case of all players set to the same field, use the first player
4812     int nr = MAX_PLAYERS - i - 1;
4813     int jx = cav->player_x[nr];
4814     int jy = cav->player_y[nr];
4815
4816     if (jx != -1 && jy != -1)
4817       level->field[jx][jy] = EL_PLAYER_1 + nr;
4818   }
4819
4820   // time score is counted for each 10 seconds left in Emerald Mine levels
4821   level->time_score_base = 10;
4822 }
4823
4824
4825 // ----------------------------------------------------------------------------
4826 // functions for loading SP level
4827 // ----------------------------------------------------------------------------
4828
4829 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4830 {
4831   struct LevelInfo_SP *level_sp = level->native_sp_level;
4832   LevelInfoType *header = &level_sp->header;
4833   int i, x, y;
4834
4835   level_sp->width  = level->fieldx;
4836   level_sp->height = level->fieldy;
4837
4838   for (x = 0; x < level->fieldx; x++)
4839     for (y = 0; y < level->fieldy; y++)
4840       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4841
4842   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4843
4844   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4845     header->LevelTitle[i] = level->name[i];
4846   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4847
4848   header->InfotronsNeeded = level->gems_needed;
4849
4850   header->SpecialPortCount = 0;
4851
4852   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4853   {
4854     boolean gravity_port_found = FALSE;
4855     boolean gravity_port_valid = FALSE;
4856     int gravity_port_flag;
4857     int gravity_port_base_element;
4858     int element = level->field[x][y];
4859
4860     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4861         element <= EL_SP_GRAVITY_ON_PORT_UP)
4862     {
4863       gravity_port_found = TRUE;
4864       gravity_port_valid = TRUE;
4865       gravity_port_flag = 1;
4866       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4867     }
4868     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4869              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4870     {
4871       gravity_port_found = TRUE;
4872       gravity_port_valid = TRUE;
4873       gravity_port_flag = 0;
4874       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4875     }
4876     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4877              element <= EL_SP_GRAVITY_PORT_UP)
4878     {
4879       // change R'n'D style gravity inverting special port to normal port
4880       // (there are no gravity inverting ports in native Supaplex engine)
4881
4882       gravity_port_found = TRUE;
4883       gravity_port_valid = FALSE;
4884       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4885     }
4886
4887     if (gravity_port_found)
4888     {
4889       if (gravity_port_valid &&
4890           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4891       {
4892         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4893
4894         port->PortLocation = (y * level->fieldx + x) * 2;
4895         port->Gravity = gravity_port_flag;
4896
4897         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4898
4899         header->SpecialPortCount++;
4900       }
4901       else
4902       {
4903         // change special gravity port to normal port
4904
4905         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4906       }
4907
4908       level_sp->playfield[x][y] = element - EL_SP_START;
4909     }
4910   }
4911 }
4912
4913 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4914 {
4915   struct LevelInfo_SP *level_sp = level->native_sp_level;
4916   LevelInfoType *header = &level_sp->header;
4917   boolean num_invalid_elements = 0;
4918   int i, j, x, y;
4919
4920   level->fieldx = level_sp->width;
4921   level->fieldy = level_sp->height;
4922
4923   for (x = 0; x < level->fieldx; x++)
4924   {
4925     for (y = 0; y < level->fieldy; y++)
4926     {
4927       int element_old = level_sp->playfield[x][y];
4928       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4929
4930       if (element_new == EL_UNKNOWN)
4931       {
4932         num_invalid_elements++;
4933
4934         Debug("level:native:SP", "invalid element %d at position %d, %d",
4935               element_old, x, y);
4936       }
4937
4938       level->field[x][y] = element_new;
4939     }
4940   }
4941
4942   if (num_invalid_elements > 0)
4943     Warn("found %d invalid elements%s", num_invalid_elements,
4944          (!options.debug ? " (use '--debug' for more details)" : ""));
4945
4946   for (i = 0; i < MAX_PLAYERS; i++)
4947     level->initial_player_gravity[i] =
4948       (header->InitialGravity == 1 ? TRUE : FALSE);
4949
4950   // skip leading spaces
4951   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4952     if (header->LevelTitle[i] != ' ')
4953       break;
4954
4955   // copy level title
4956   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4957     level->name[j] = header->LevelTitle[i];
4958   level->name[j] = '\0';
4959
4960   // cut trailing spaces
4961   for (; j > 0; j--)
4962     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4963       level->name[j - 1] = '\0';
4964
4965   level->gems_needed = header->InfotronsNeeded;
4966
4967   for (i = 0; i < header->SpecialPortCount; i++)
4968   {
4969     SpecialPortType *port = &header->SpecialPort[i];
4970     int port_location = port->PortLocation;
4971     int gravity = port->Gravity;
4972     int port_x, port_y, port_element;
4973
4974     port_x = (port_location / 2) % level->fieldx;
4975     port_y = (port_location / 2) / level->fieldx;
4976
4977     if (port_x < 0 || port_x >= level->fieldx ||
4978         port_y < 0 || port_y >= level->fieldy)
4979     {
4980       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4981
4982       continue;
4983     }
4984
4985     port_element = level->field[port_x][port_y];
4986
4987     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4988         port_element > EL_SP_GRAVITY_PORT_UP)
4989     {
4990       Warn("no special port at position (%d, %d)", port_x, port_y);
4991
4992       continue;
4993     }
4994
4995     // change previous (wrong) gravity inverting special port to either
4996     // gravity enabling special port or gravity disabling special port
4997     level->field[port_x][port_y] +=
4998       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4999        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5000   }
5001
5002   // change special gravity ports without database entries to normal ports
5003   for (x = 0; x < level->fieldx; x++)
5004     for (y = 0; y < level->fieldy; y++)
5005       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5006           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5007         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5008
5009   level->time = 0;                      // no time limit
5010   level->amoeba_speed = 0;
5011   level->time_magic_wall = 0;
5012   level->time_wheel = 0;
5013   level->amoeba_content = EL_EMPTY;
5014
5015   // original Supaplex does not use score values -- rate by playing time
5016   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5017     level->score[i] = 0;
5018
5019   level->rate_time_over_score = TRUE;
5020
5021   // there are no yamyams in supaplex levels
5022   for (i = 0; i < level->num_yamyam_contents; i++)
5023     for (x = 0; x < 3; x++)
5024       for (y = 0; y < 3; y++)
5025         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5026 }
5027
5028 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5029 {
5030   struct LevelInfo_SP *level_sp = level->native_sp_level;
5031   struct DemoInfo_SP *demo = &level_sp->demo;
5032   int i, j;
5033
5034   // always start with reliable default values
5035   demo->is_available = FALSE;
5036   demo->length = 0;
5037
5038   if (TAPE_IS_EMPTY(tape))
5039     return;
5040
5041   demo->level_nr = tape.level_nr;       // (currently not used)
5042
5043   level_sp->header.DemoRandomSeed = tape.random_seed;
5044
5045   demo->length = 0;
5046
5047   for (i = 0; i < tape.length; i++)
5048   {
5049     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5050     int demo_repeat = tape.pos[i].delay;
5051     int demo_entries = (demo_repeat + 15) / 16;
5052
5053     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5054     {
5055       Warn("tape truncated: size exceeds maximum SP demo size %d",
5056            SP_MAX_TAPE_LEN);
5057
5058       break;
5059     }
5060
5061     for (j = 0; j < demo_repeat / 16; j++)
5062       demo->data[demo->length++] = 0xf0 | demo_action;
5063
5064     if (demo_repeat % 16)
5065       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5066   }
5067
5068   demo->is_available = TRUE;
5069 }
5070
5071 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5072 {
5073   struct LevelInfo_SP *level_sp = level->native_sp_level;
5074   struct DemoInfo_SP *demo = &level_sp->demo;
5075   char *filename = level->file_info.filename;
5076   int i;
5077
5078   // always start with reliable default values
5079   setTapeInfoToDefaults();
5080
5081   if (!demo->is_available)
5082     return;
5083
5084   tape.level_nr = demo->level_nr;       // (currently not used)
5085   tape.random_seed = level_sp->header.DemoRandomSeed;
5086
5087   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5088
5089   tape.counter = 0;
5090   tape.pos[tape.counter].delay = 0;
5091
5092   for (i = 0; i < demo->length; i++)
5093   {
5094     int demo_action = demo->data[i] & 0x0f;
5095     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5096     int tape_action = map_key_SP_to_RND(demo_action);
5097     int tape_repeat = demo_repeat + 1;
5098     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5099     boolean success = 0;
5100     int j;
5101
5102     for (j = 0; j < tape_repeat; j++)
5103       success = TapeAddAction(action);
5104
5105     if (!success)
5106     {
5107       Warn("SP demo truncated: size exceeds maximum tape size %d",
5108            MAX_TAPE_LEN);
5109
5110       break;
5111     }
5112   }
5113
5114   TapeHaltRecording();
5115 }
5116
5117
5118 // ----------------------------------------------------------------------------
5119 // functions for loading MM level
5120 // ----------------------------------------------------------------------------
5121
5122 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5123 {
5124   struct LevelInfo_MM *level_mm = level->native_mm_level;
5125   int i, x, y;
5126
5127   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5128   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5129
5130   level_mm->time = level->time;
5131   level_mm->kettles_needed = level->gems_needed;
5132   level_mm->auto_count_kettles = level->auto_count_gems;
5133
5134   level_mm->mm_laser_red   = level->mm_laser_red;
5135   level_mm->mm_laser_green = level->mm_laser_green;
5136   level_mm->mm_laser_blue  = level->mm_laser_blue;
5137
5138   level_mm->df_laser_red   = level->df_laser_red;
5139   level_mm->df_laser_green = level->df_laser_green;
5140   level_mm->df_laser_blue  = level->df_laser_blue;
5141
5142   strcpy(level_mm->name, level->name);
5143   strcpy(level_mm->author, level->author);
5144
5145   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5146   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5147   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5148   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5149   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5150
5151   level_mm->amoeba_speed = level->amoeba_speed;
5152   level_mm->time_fuse    = level->mm_time_fuse;
5153   level_mm->time_bomb    = level->mm_time_bomb;
5154   level_mm->time_ball    = level->mm_time_ball;
5155   level_mm->time_block   = level->mm_time_block;
5156
5157   level_mm->num_ball_contents = level->num_mm_ball_contents;
5158   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5159   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5160   level_mm->explode_ball = level->explode_mm_ball;
5161
5162   for (i = 0; i < level->num_mm_ball_contents; i++)
5163     level_mm->ball_content[i] =
5164       map_element_RND_to_MM(level->mm_ball_content[i]);
5165
5166   for (x = 0; x < level->fieldx; x++)
5167     for (y = 0; y < level->fieldy; y++)
5168       Ur[x][y] =
5169         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5170 }
5171
5172 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5173 {
5174   struct LevelInfo_MM *level_mm = level->native_mm_level;
5175   int i, x, y;
5176
5177   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5178   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5179
5180   level->time = level_mm->time;
5181   level->gems_needed = level_mm->kettles_needed;
5182   level->auto_count_gems = level_mm->auto_count_kettles;
5183
5184   level->mm_laser_red   = level_mm->mm_laser_red;
5185   level->mm_laser_green = level_mm->mm_laser_green;
5186   level->mm_laser_blue  = level_mm->mm_laser_blue;
5187
5188   level->df_laser_red   = level_mm->df_laser_red;
5189   level->df_laser_green = level_mm->df_laser_green;
5190   level->df_laser_blue  = level_mm->df_laser_blue;
5191
5192   strcpy(level->name, level_mm->name);
5193
5194   // only overwrite author from 'levelinfo.conf' if author defined in level
5195   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5196     strcpy(level->author, level_mm->author);
5197
5198   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5199   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5200   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5201   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5202   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5203
5204   level->amoeba_speed  = level_mm->amoeba_speed;
5205   level->mm_time_fuse  = level_mm->time_fuse;
5206   level->mm_time_bomb  = level_mm->time_bomb;
5207   level->mm_time_ball  = level_mm->time_ball;
5208   level->mm_time_block = level_mm->time_block;
5209
5210   level->num_mm_ball_contents = level_mm->num_ball_contents;
5211   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5212   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5213   level->explode_mm_ball = level_mm->explode_ball;
5214
5215   for (i = 0; i < level->num_mm_ball_contents; i++)
5216     level->mm_ball_content[i] =
5217       map_element_MM_to_RND(level_mm->ball_content[i]);
5218
5219   for (x = 0; x < level->fieldx; x++)
5220     for (y = 0; y < level->fieldy; y++)
5221       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5222 }
5223
5224
5225 // ----------------------------------------------------------------------------
5226 // functions for loading DC level
5227 // ----------------------------------------------------------------------------
5228
5229 #define DC_LEVEL_HEADER_SIZE            344
5230
5231 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5232                                         boolean init)
5233 {
5234   static int last_data_encoded;
5235   static int offset1;
5236   static int offset2;
5237   int diff;
5238   int diff_hi, diff_lo;
5239   int data_hi, data_lo;
5240   unsigned short data_decoded;
5241
5242   if (init)
5243   {
5244     last_data_encoded = 0;
5245     offset1 = -1;
5246     offset2 = 0;
5247
5248     return 0;
5249   }
5250
5251   diff = data_encoded - last_data_encoded;
5252   diff_hi = diff & ~0xff;
5253   diff_lo = diff &  0xff;
5254
5255   offset2 += diff_lo;
5256
5257   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5258   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5259   data_hi = data_hi & 0xff00;
5260
5261   data_decoded = data_hi | data_lo;
5262
5263   last_data_encoded = data_encoded;
5264
5265   offset1 = (offset1 + 1) % 31;
5266   offset2 = offset2 & 0xff;
5267
5268   return data_decoded;
5269 }
5270
5271 static int getMappedElement_DC(int element)
5272 {
5273   switch (element)
5274   {
5275     case 0x0000:
5276       element = EL_ROCK;
5277       break;
5278
5279       // 0x0117 - 0x036e: (?)
5280       // EL_DIAMOND
5281
5282       // 0x042d - 0x0684: (?)
5283       // EL_EMERALD
5284
5285     case 0x06f1:
5286       element = EL_NUT;
5287       break;
5288
5289     case 0x074c:
5290       element = EL_BOMB;
5291       break;
5292
5293     case 0x07a4:
5294       element = EL_PEARL;
5295       break;
5296
5297     case 0x0823:
5298       element = EL_CRYSTAL;
5299       break;
5300
5301     case 0x0e77:        // quicksand (boulder)
5302       element = EL_QUICKSAND_FAST_FULL;
5303       break;
5304
5305     case 0x0e99:        // slow quicksand (boulder)
5306       element = EL_QUICKSAND_FULL;
5307       break;
5308
5309     case 0x0ed2:
5310       element = EL_EM_EXIT_OPEN;
5311       break;
5312
5313     case 0x0ee3:
5314       element = EL_EM_EXIT_CLOSED;
5315       break;
5316
5317     case 0x0eeb:
5318       element = EL_EM_STEEL_EXIT_OPEN;
5319       break;
5320
5321     case 0x0efc:
5322       element = EL_EM_STEEL_EXIT_CLOSED;
5323       break;
5324
5325     case 0x0f4f:        // dynamite (lit 1)
5326       element = EL_EM_DYNAMITE_ACTIVE;
5327       break;
5328
5329     case 0x0f57:        // dynamite (lit 2)
5330       element = EL_EM_DYNAMITE_ACTIVE;
5331       break;
5332
5333     case 0x0f5f:        // dynamite (lit 3)
5334       element = EL_EM_DYNAMITE_ACTIVE;
5335       break;
5336
5337     case 0x0f67:        // dynamite (lit 4)
5338       element = EL_EM_DYNAMITE_ACTIVE;
5339       break;
5340
5341     case 0x0f81:
5342     case 0x0f82:
5343     case 0x0f83:
5344     case 0x0f84:
5345       element = EL_AMOEBA_WET;
5346       break;
5347
5348     case 0x0f85:
5349       element = EL_AMOEBA_DROP;
5350       break;
5351
5352     case 0x0fb9:
5353       element = EL_DC_MAGIC_WALL;
5354       break;
5355
5356     case 0x0fd0:
5357       element = EL_SPACESHIP_UP;
5358       break;
5359
5360     case 0x0fd9:
5361       element = EL_SPACESHIP_DOWN;
5362       break;
5363
5364     case 0x0ff1:
5365       element = EL_SPACESHIP_LEFT;
5366       break;
5367
5368     case 0x0ff9:
5369       element = EL_SPACESHIP_RIGHT;
5370       break;
5371
5372     case 0x1057:
5373       element = EL_BUG_UP;
5374       break;
5375
5376     case 0x1060:
5377       element = EL_BUG_DOWN;
5378       break;
5379
5380     case 0x1078:
5381       element = EL_BUG_LEFT;
5382       break;
5383
5384     case 0x1080:
5385       element = EL_BUG_RIGHT;
5386       break;
5387
5388     case 0x10de:
5389       element = EL_MOLE_UP;
5390       break;
5391
5392     case 0x10e7:
5393       element = EL_MOLE_DOWN;
5394       break;
5395
5396     case 0x10ff:
5397       element = EL_MOLE_LEFT;
5398       break;
5399
5400     case 0x1107:
5401       element = EL_MOLE_RIGHT;
5402       break;
5403
5404     case 0x11c0:
5405       element = EL_ROBOT;
5406       break;
5407
5408     case 0x13f5:
5409       element = EL_YAMYAM_UP;
5410       break;
5411
5412     case 0x1425:
5413       element = EL_SWITCHGATE_OPEN;
5414       break;
5415
5416     case 0x1426:
5417       element = EL_SWITCHGATE_CLOSED;
5418       break;
5419
5420     case 0x1437:
5421       element = EL_DC_SWITCHGATE_SWITCH_UP;
5422       break;
5423
5424     case 0x143a:
5425       element = EL_TIMEGATE_CLOSED;
5426       break;
5427
5428     case 0x144c:        // conveyor belt switch (green)
5429       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5430       break;
5431
5432     case 0x144f:        // conveyor belt switch (red)
5433       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5434       break;
5435
5436     case 0x1452:        // conveyor belt switch (blue)
5437       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5438       break;
5439
5440     case 0x145b:
5441       element = EL_CONVEYOR_BELT_3_MIDDLE;
5442       break;
5443
5444     case 0x1463:
5445       element = EL_CONVEYOR_BELT_3_LEFT;
5446       break;
5447
5448     case 0x146b:
5449       element = EL_CONVEYOR_BELT_3_RIGHT;
5450       break;
5451
5452     case 0x1473:
5453       element = EL_CONVEYOR_BELT_1_MIDDLE;
5454       break;
5455
5456     case 0x147b:
5457       element = EL_CONVEYOR_BELT_1_LEFT;
5458       break;
5459
5460     case 0x1483:
5461       element = EL_CONVEYOR_BELT_1_RIGHT;
5462       break;
5463
5464     case 0x148b:
5465       element = EL_CONVEYOR_BELT_4_MIDDLE;
5466       break;
5467
5468     case 0x1493:
5469       element = EL_CONVEYOR_BELT_4_LEFT;
5470       break;
5471
5472     case 0x149b:
5473       element = EL_CONVEYOR_BELT_4_RIGHT;
5474       break;
5475
5476     case 0x14ac:
5477       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5478       break;
5479
5480     case 0x14bd:
5481       element = EL_EXPANDABLE_WALL_VERTICAL;
5482       break;
5483
5484     case 0x14c6:
5485       element = EL_EXPANDABLE_WALL_ANY;
5486       break;
5487
5488     case 0x14ce:        // growing steel wall (left/right)
5489       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5490       break;
5491
5492     case 0x14df:        // growing steel wall (up/down)
5493       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5494       break;
5495
5496     case 0x14e8:        // growing steel wall (up/down/left/right)
5497       element = EL_EXPANDABLE_STEELWALL_ANY;
5498       break;
5499
5500     case 0x14e9:
5501       element = EL_SHIELD_DEADLY;
5502       break;
5503
5504     case 0x1501:
5505       element = EL_EXTRA_TIME;
5506       break;
5507
5508     case 0x154f:
5509       element = EL_ACID;
5510       break;
5511
5512     case 0x1577:
5513       element = EL_EMPTY_SPACE;
5514       break;
5515
5516     case 0x1578:        // quicksand (empty)
5517       element = EL_QUICKSAND_FAST_EMPTY;
5518       break;
5519
5520     case 0x1579:        // slow quicksand (empty)
5521       element = EL_QUICKSAND_EMPTY;
5522       break;
5523
5524       // 0x157c - 0x158b:
5525       // EL_SAND
5526
5527       // 0x1590 - 0x159f:
5528       // EL_DC_LANDMINE
5529
5530     case 0x15a0:
5531       element = EL_EM_DYNAMITE;
5532       break;
5533
5534     case 0x15a1:        // key (red)
5535       element = EL_EM_KEY_1;
5536       break;
5537
5538     case 0x15a2:        // key (yellow)
5539       element = EL_EM_KEY_2;
5540       break;
5541
5542     case 0x15a3:        // key (blue)
5543       element = EL_EM_KEY_4;
5544       break;
5545
5546     case 0x15a4:        // key (green)
5547       element = EL_EM_KEY_3;
5548       break;
5549
5550     case 0x15a5:        // key (white)
5551       element = EL_DC_KEY_WHITE;
5552       break;
5553
5554     case 0x15a6:
5555       element = EL_WALL_SLIPPERY;
5556       break;
5557
5558     case 0x15a7:
5559       element = EL_WALL;
5560       break;
5561
5562     case 0x15a8:        // wall (not round)
5563       element = EL_WALL;
5564       break;
5565
5566     case 0x15a9:        // (blue)
5567       element = EL_CHAR_A;
5568       break;
5569
5570     case 0x15aa:        // (blue)
5571       element = EL_CHAR_B;
5572       break;
5573
5574     case 0x15ab:        // (blue)
5575       element = EL_CHAR_C;
5576       break;
5577
5578     case 0x15ac:        // (blue)
5579       element = EL_CHAR_D;
5580       break;
5581
5582     case 0x15ad:        // (blue)
5583       element = EL_CHAR_E;
5584       break;
5585
5586     case 0x15ae:        // (blue)
5587       element = EL_CHAR_F;
5588       break;
5589
5590     case 0x15af:        // (blue)
5591       element = EL_CHAR_G;
5592       break;
5593
5594     case 0x15b0:        // (blue)
5595       element = EL_CHAR_H;
5596       break;
5597
5598     case 0x15b1:        // (blue)
5599       element = EL_CHAR_I;
5600       break;
5601
5602     case 0x15b2:        // (blue)
5603       element = EL_CHAR_J;
5604       break;
5605
5606     case 0x15b3:        // (blue)
5607       element = EL_CHAR_K;
5608       break;
5609
5610     case 0x15b4:        // (blue)
5611       element = EL_CHAR_L;
5612       break;
5613
5614     case 0x15b5:        // (blue)
5615       element = EL_CHAR_M;
5616       break;
5617
5618     case 0x15b6:        // (blue)
5619       element = EL_CHAR_N;
5620       break;
5621
5622     case 0x15b7:        // (blue)
5623       element = EL_CHAR_O;
5624       break;
5625
5626     case 0x15b8:        // (blue)
5627       element = EL_CHAR_P;
5628       break;
5629
5630     case 0x15b9:        // (blue)
5631       element = EL_CHAR_Q;
5632       break;
5633
5634     case 0x15ba:        // (blue)
5635       element = EL_CHAR_R;
5636       break;
5637
5638     case 0x15bb:        // (blue)
5639       element = EL_CHAR_S;
5640       break;
5641
5642     case 0x15bc:        // (blue)
5643       element = EL_CHAR_T;
5644       break;
5645
5646     case 0x15bd:        // (blue)
5647       element = EL_CHAR_U;
5648       break;
5649
5650     case 0x15be:        // (blue)
5651       element = EL_CHAR_V;
5652       break;
5653
5654     case 0x15bf:        // (blue)
5655       element = EL_CHAR_W;
5656       break;
5657
5658     case 0x15c0:        // (blue)
5659       element = EL_CHAR_X;
5660       break;
5661
5662     case 0x15c1:        // (blue)
5663       element = EL_CHAR_Y;
5664       break;
5665
5666     case 0x15c2:        // (blue)
5667       element = EL_CHAR_Z;
5668       break;
5669
5670     case 0x15c3:        // (blue)
5671       element = EL_CHAR_AUMLAUT;
5672       break;
5673
5674     case 0x15c4:        // (blue)
5675       element = EL_CHAR_OUMLAUT;
5676       break;
5677
5678     case 0x15c5:        // (blue)
5679       element = EL_CHAR_UUMLAUT;
5680       break;
5681
5682     case 0x15c6:        // (blue)
5683       element = EL_CHAR_0;
5684       break;
5685
5686     case 0x15c7:        // (blue)
5687       element = EL_CHAR_1;
5688       break;
5689
5690     case 0x15c8:        // (blue)
5691       element = EL_CHAR_2;
5692       break;
5693
5694     case 0x15c9:        // (blue)
5695       element = EL_CHAR_3;
5696       break;
5697
5698     case 0x15ca:        // (blue)
5699       element = EL_CHAR_4;
5700       break;
5701
5702     case 0x15cb:        // (blue)
5703       element = EL_CHAR_5;
5704       break;
5705
5706     case 0x15cc:        // (blue)
5707       element = EL_CHAR_6;
5708       break;
5709
5710     case 0x15cd:        // (blue)
5711       element = EL_CHAR_7;
5712       break;
5713
5714     case 0x15ce:        // (blue)
5715       element = EL_CHAR_8;
5716       break;
5717
5718     case 0x15cf:        // (blue)
5719       element = EL_CHAR_9;
5720       break;
5721
5722     case 0x15d0:        // (blue)
5723       element = EL_CHAR_PERIOD;
5724       break;
5725
5726     case 0x15d1:        // (blue)
5727       element = EL_CHAR_EXCLAM;
5728       break;
5729
5730     case 0x15d2:        // (blue)
5731       element = EL_CHAR_COLON;
5732       break;
5733
5734     case 0x15d3:        // (blue)
5735       element = EL_CHAR_LESS;
5736       break;
5737
5738     case 0x15d4:        // (blue)
5739       element = EL_CHAR_GREATER;
5740       break;
5741
5742     case 0x15d5:        // (blue)
5743       element = EL_CHAR_QUESTION;
5744       break;
5745
5746     case 0x15d6:        // (blue)
5747       element = EL_CHAR_COPYRIGHT;
5748       break;
5749
5750     case 0x15d7:        // (blue)
5751       element = EL_CHAR_UP;
5752       break;
5753
5754     case 0x15d8:        // (blue)
5755       element = EL_CHAR_DOWN;
5756       break;
5757
5758     case 0x15d9:        // (blue)
5759       element = EL_CHAR_BUTTON;
5760       break;
5761
5762     case 0x15da:        // (blue)
5763       element = EL_CHAR_PLUS;
5764       break;
5765
5766     case 0x15db:        // (blue)
5767       element = EL_CHAR_MINUS;
5768       break;
5769
5770     case 0x15dc:        // (blue)
5771       element = EL_CHAR_APOSTROPHE;
5772       break;
5773
5774     case 0x15dd:        // (blue)
5775       element = EL_CHAR_PARENLEFT;
5776       break;
5777
5778     case 0x15de:        // (blue)
5779       element = EL_CHAR_PARENRIGHT;
5780       break;
5781
5782     case 0x15df:        // (green)
5783       element = EL_CHAR_A;
5784       break;
5785
5786     case 0x15e0:        // (green)
5787       element = EL_CHAR_B;
5788       break;
5789
5790     case 0x15e1:        // (green)
5791       element = EL_CHAR_C;
5792       break;
5793
5794     case 0x15e2:        // (green)
5795       element = EL_CHAR_D;
5796       break;
5797
5798     case 0x15e3:        // (green)
5799       element = EL_CHAR_E;
5800       break;
5801
5802     case 0x15e4:        // (green)
5803       element = EL_CHAR_F;
5804       break;
5805
5806     case 0x15e5:        // (green)
5807       element = EL_CHAR_G;
5808       break;
5809
5810     case 0x15e6:        // (green)
5811       element = EL_CHAR_H;
5812       break;
5813
5814     case 0x15e7:        // (green)
5815       element = EL_CHAR_I;
5816       break;
5817
5818     case 0x15e8:        // (green)
5819       element = EL_CHAR_J;
5820       break;
5821
5822     case 0x15e9:        // (green)
5823       element = EL_CHAR_K;
5824       break;
5825
5826     case 0x15ea:        // (green)
5827       element = EL_CHAR_L;
5828       break;
5829
5830     case 0x15eb:        // (green)
5831       element = EL_CHAR_M;
5832       break;
5833
5834     case 0x15ec:        // (green)
5835       element = EL_CHAR_N;
5836       break;
5837
5838     case 0x15ed:        // (green)
5839       element = EL_CHAR_O;
5840       break;
5841
5842     case 0x15ee:        // (green)
5843       element = EL_CHAR_P;
5844       break;
5845
5846     case 0x15ef:        // (green)
5847       element = EL_CHAR_Q;
5848       break;
5849
5850     case 0x15f0:        // (green)
5851       element = EL_CHAR_R;
5852       break;
5853
5854     case 0x15f1:        // (green)
5855       element = EL_CHAR_S;
5856       break;
5857
5858     case 0x15f2:        // (green)
5859       element = EL_CHAR_T;
5860       break;
5861
5862     case 0x15f3:        // (green)
5863       element = EL_CHAR_U;
5864       break;
5865
5866     case 0x15f4:        // (green)
5867       element = EL_CHAR_V;
5868       break;
5869
5870     case 0x15f5:        // (green)
5871       element = EL_CHAR_W;
5872       break;
5873
5874     case 0x15f6:        // (green)
5875       element = EL_CHAR_X;
5876       break;
5877
5878     case 0x15f7:        // (green)
5879       element = EL_CHAR_Y;
5880       break;
5881
5882     case 0x15f8:        // (green)
5883       element = EL_CHAR_Z;
5884       break;
5885
5886     case 0x15f9:        // (green)
5887       element = EL_CHAR_AUMLAUT;
5888       break;
5889
5890     case 0x15fa:        // (green)
5891       element = EL_CHAR_OUMLAUT;
5892       break;
5893
5894     case 0x15fb:        // (green)
5895       element = EL_CHAR_UUMLAUT;
5896       break;
5897
5898     case 0x15fc:        // (green)
5899       element = EL_CHAR_0;
5900       break;
5901
5902     case 0x15fd:        // (green)
5903       element = EL_CHAR_1;
5904       break;
5905
5906     case 0x15fe:        // (green)
5907       element = EL_CHAR_2;
5908       break;
5909
5910     case 0x15ff:        // (green)
5911       element = EL_CHAR_3;
5912       break;
5913
5914     case 0x1600:        // (green)
5915       element = EL_CHAR_4;
5916       break;
5917
5918     case 0x1601:        // (green)
5919       element = EL_CHAR_5;
5920       break;
5921
5922     case 0x1602:        // (green)
5923       element = EL_CHAR_6;
5924       break;
5925
5926     case 0x1603:        // (green)
5927       element = EL_CHAR_7;
5928       break;
5929
5930     case 0x1604:        // (green)
5931       element = EL_CHAR_8;
5932       break;
5933
5934     case 0x1605:        // (green)
5935       element = EL_CHAR_9;
5936       break;
5937
5938     case 0x1606:        // (green)
5939       element = EL_CHAR_PERIOD;
5940       break;
5941
5942     case 0x1607:        // (green)
5943       element = EL_CHAR_EXCLAM;
5944       break;
5945
5946     case 0x1608:        // (green)
5947       element = EL_CHAR_COLON;
5948       break;
5949
5950     case 0x1609:        // (green)
5951       element = EL_CHAR_LESS;
5952       break;
5953
5954     case 0x160a:        // (green)
5955       element = EL_CHAR_GREATER;
5956       break;
5957
5958     case 0x160b:        // (green)
5959       element = EL_CHAR_QUESTION;
5960       break;
5961
5962     case 0x160c:        // (green)
5963       element = EL_CHAR_COPYRIGHT;
5964       break;
5965
5966     case 0x160d:        // (green)
5967       element = EL_CHAR_UP;
5968       break;
5969
5970     case 0x160e:        // (green)
5971       element = EL_CHAR_DOWN;
5972       break;
5973
5974     case 0x160f:        // (green)
5975       element = EL_CHAR_BUTTON;
5976       break;
5977
5978     case 0x1610:        // (green)
5979       element = EL_CHAR_PLUS;
5980       break;
5981
5982     case 0x1611:        // (green)
5983       element = EL_CHAR_MINUS;
5984       break;
5985
5986     case 0x1612:        // (green)
5987       element = EL_CHAR_APOSTROPHE;
5988       break;
5989
5990     case 0x1613:        // (green)
5991       element = EL_CHAR_PARENLEFT;
5992       break;
5993
5994     case 0x1614:        // (green)
5995       element = EL_CHAR_PARENRIGHT;
5996       break;
5997
5998     case 0x1615:        // (blue steel)
5999       element = EL_STEEL_CHAR_A;
6000       break;
6001
6002     case 0x1616:        // (blue steel)
6003       element = EL_STEEL_CHAR_B;
6004       break;
6005
6006     case 0x1617:        // (blue steel)
6007       element = EL_STEEL_CHAR_C;
6008       break;
6009
6010     case 0x1618:        // (blue steel)
6011       element = EL_STEEL_CHAR_D;
6012       break;
6013
6014     case 0x1619:        // (blue steel)
6015       element = EL_STEEL_CHAR_E;
6016       break;
6017
6018     case 0x161a:        // (blue steel)
6019       element = EL_STEEL_CHAR_F;
6020       break;
6021
6022     case 0x161b:        // (blue steel)
6023       element = EL_STEEL_CHAR_G;
6024       break;
6025
6026     case 0x161c:        // (blue steel)
6027       element = EL_STEEL_CHAR_H;
6028       break;
6029
6030     case 0x161d:        // (blue steel)
6031       element = EL_STEEL_CHAR_I;
6032       break;
6033
6034     case 0x161e:        // (blue steel)
6035       element = EL_STEEL_CHAR_J;
6036       break;
6037
6038     case 0x161f:        // (blue steel)
6039       element = EL_STEEL_CHAR_K;
6040       break;
6041
6042     case 0x1620:        // (blue steel)
6043       element = EL_STEEL_CHAR_L;
6044       break;
6045
6046     case 0x1621:        // (blue steel)
6047       element = EL_STEEL_CHAR_M;
6048       break;
6049
6050     case 0x1622:        // (blue steel)
6051       element = EL_STEEL_CHAR_N;
6052       break;
6053
6054     case 0x1623:        // (blue steel)
6055       element = EL_STEEL_CHAR_O;
6056       break;
6057
6058     case 0x1624:        // (blue steel)
6059       element = EL_STEEL_CHAR_P;
6060       break;
6061
6062     case 0x1625:        // (blue steel)
6063       element = EL_STEEL_CHAR_Q;
6064       break;
6065
6066     case 0x1626:        // (blue steel)
6067       element = EL_STEEL_CHAR_R;
6068       break;
6069
6070     case 0x1627:        // (blue steel)
6071       element = EL_STEEL_CHAR_S;
6072       break;
6073
6074     case 0x1628:        // (blue steel)
6075       element = EL_STEEL_CHAR_T;
6076       break;
6077
6078     case 0x1629:        // (blue steel)
6079       element = EL_STEEL_CHAR_U;
6080       break;
6081
6082     case 0x162a:        // (blue steel)
6083       element = EL_STEEL_CHAR_V;
6084       break;
6085
6086     case 0x162b:        // (blue steel)
6087       element = EL_STEEL_CHAR_W;
6088       break;
6089
6090     case 0x162c:        // (blue steel)
6091       element = EL_STEEL_CHAR_X;
6092       break;
6093
6094     case 0x162d:        // (blue steel)
6095       element = EL_STEEL_CHAR_Y;
6096       break;
6097
6098     case 0x162e:        // (blue steel)
6099       element = EL_STEEL_CHAR_Z;
6100       break;
6101
6102     case 0x162f:        // (blue steel)
6103       element = EL_STEEL_CHAR_AUMLAUT;
6104       break;
6105
6106     case 0x1630:        // (blue steel)
6107       element = EL_STEEL_CHAR_OUMLAUT;
6108       break;
6109
6110     case 0x1631:        // (blue steel)
6111       element = EL_STEEL_CHAR_UUMLAUT;
6112       break;
6113
6114     case 0x1632:        // (blue steel)
6115       element = EL_STEEL_CHAR_0;
6116       break;
6117
6118     case 0x1633:        // (blue steel)
6119       element = EL_STEEL_CHAR_1;
6120       break;
6121
6122     case 0x1634:        // (blue steel)
6123       element = EL_STEEL_CHAR_2;
6124       break;
6125
6126     case 0x1635:        // (blue steel)
6127       element = EL_STEEL_CHAR_3;
6128       break;
6129
6130     case 0x1636:        // (blue steel)
6131       element = EL_STEEL_CHAR_4;
6132       break;
6133
6134     case 0x1637:        // (blue steel)
6135       element = EL_STEEL_CHAR_5;
6136       break;
6137
6138     case 0x1638:        // (blue steel)
6139       element = EL_STEEL_CHAR_6;
6140       break;
6141
6142     case 0x1639:        // (blue steel)
6143       element = EL_STEEL_CHAR_7;
6144       break;
6145
6146     case 0x163a:        // (blue steel)
6147       element = EL_STEEL_CHAR_8;
6148       break;
6149
6150     case 0x163b:        // (blue steel)
6151       element = EL_STEEL_CHAR_9;
6152       break;
6153
6154     case 0x163c:        // (blue steel)
6155       element = EL_STEEL_CHAR_PERIOD;
6156       break;
6157
6158     case 0x163d:        // (blue steel)
6159       element = EL_STEEL_CHAR_EXCLAM;
6160       break;
6161
6162     case 0x163e:        // (blue steel)
6163       element = EL_STEEL_CHAR_COLON;
6164       break;
6165
6166     case 0x163f:        // (blue steel)
6167       element = EL_STEEL_CHAR_LESS;
6168       break;
6169
6170     case 0x1640:        // (blue steel)
6171       element = EL_STEEL_CHAR_GREATER;
6172       break;
6173
6174     case 0x1641:        // (blue steel)
6175       element = EL_STEEL_CHAR_QUESTION;
6176       break;
6177
6178     case 0x1642:        // (blue steel)
6179       element = EL_STEEL_CHAR_COPYRIGHT;
6180       break;
6181
6182     case 0x1643:        // (blue steel)
6183       element = EL_STEEL_CHAR_UP;
6184       break;
6185
6186     case 0x1644:        // (blue steel)
6187       element = EL_STEEL_CHAR_DOWN;
6188       break;
6189
6190     case 0x1645:        // (blue steel)
6191       element = EL_STEEL_CHAR_BUTTON;
6192       break;
6193
6194     case 0x1646:        // (blue steel)
6195       element = EL_STEEL_CHAR_PLUS;
6196       break;
6197
6198     case 0x1647:        // (blue steel)
6199       element = EL_STEEL_CHAR_MINUS;
6200       break;
6201
6202     case 0x1648:        // (blue steel)
6203       element = EL_STEEL_CHAR_APOSTROPHE;
6204       break;
6205
6206     case 0x1649:        // (blue steel)
6207       element = EL_STEEL_CHAR_PARENLEFT;
6208       break;
6209
6210     case 0x164a:        // (blue steel)
6211       element = EL_STEEL_CHAR_PARENRIGHT;
6212       break;
6213
6214     case 0x164b:        // (green steel)
6215       element = EL_STEEL_CHAR_A;
6216       break;
6217
6218     case 0x164c:        // (green steel)
6219       element = EL_STEEL_CHAR_B;
6220       break;
6221
6222     case 0x164d:        // (green steel)
6223       element = EL_STEEL_CHAR_C;
6224       break;
6225
6226     case 0x164e:        // (green steel)
6227       element = EL_STEEL_CHAR_D;
6228       break;
6229
6230     case 0x164f:        // (green steel)
6231       element = EL_STEEL_CHAR_E;
6232       break;
6233
6234     case 0x1650:        // (green steel)
6235       element = EL_STEEL_CHAR_F;
6236       break;
6237
6238     case 0x1651:        // (green steel)
6239       element = EL_STEEL_CHAR_G;
6240       break;
6241
6242     case 0x1652:        // (green steel)
6243       element = EL_STEEL_CHAR_H;
6244       break;
6245
6246     case 0x1653:        // (green steel)
6247       element = EL_STEEL_CHAR_I;
6248       break;
6249
6250     case 0x1654:        // (green steel)
6251       element = EL_STEEL_CHAR_J;
6252       break;
6253
6254     case 0x1655:        // (green steel)
6255       element = EL_STEEL_CHAR_K;
6256       break;
6257
6258     case 0x1656:        // (green steel)
6259       element = EL_STEEL_CHAR_L;
6260       break;
6261
6262     case 0x1657:        // (green steel)
6263       element = EL_STEEL_CHAR_M;
6264       break;
6265
6266     case 0x1658:        // (green steel)
6267       element = EL_STEEL_CHAR_N;
6268       break;
6269
6270     case 0x1659:        // (green steel)
6271       element = EL_STEEL_CHAR_O;
6272       break;
6273
6274     case 0x165a:        // (green steel)
6275       element = EL_STEEL_CHAR_P;
6276       break;
6277
6278     case 0x165b:        // (green steel)
6279       element = EL_STEEL_CHAR_Q;
6280       break;
6281
6282     case 0x165c:        // (green steel)
6283       element = EL_STEEL_CHAR_R;
6284       break;
6285
6286     case 0x165d:        // (green steel)
6287       element = EL_STEEL_CHAR_S;
6288       break;
6289
6290     case 0x165e:        // (green steel)
6291       element = EL_STEEL_CHAR_T;
6292       break;
6293
6294     case 0x165f:        // (green steel)
6295       element = EL_STEEL_CHAR_U;
6296       break;
6297
6298     case 0x1660:        // (green steel)
6299       element = EL_STEEL_CHAR_V;
6300       break;
6301
6302     case 0x1661:        // (green steel)
6303       element = EL_STEEL_CHAR_W;
6304       break;
6305
6306     case 0x1662:        // (green steel)
6307       element = EL_STEEL_CHAR_X;
6308       break;
6309
6310     case 0x1663:        // (green steel)
6311       element = EL_STEEL_CHAR_Y;
6312       break;
6313
6314     case 0x1664:        // (green steel)
6315       element = EL_STEEL_CHAR_Z;
6316       break;
6317
6318     case 0x1665:        // (green steel)
6319       element = EL_STEEL_CHAR_AUMLAUT;
6320       break;
6321
6322     case 0x1666:        // (green steel)
6323       element = EL_STEEL_CHAR_OUMLAUT;
6324       break;
6325
6326     case 0x1667:        // (green steel)
6327       element = EL_STEEL_CHAR_UUMLAUT;
6328       break;
6329
6330     case 0x1668:        // (green steel)
6331       element = EL_STEEL_CHAR_0;
6332       break;
6333
6334     case 0x1669:        // (green steel)
6335       element = EL_STEEL_CHAR_1;
6336       break;
6337
6338     case 0x166a:        // (green steel)
6339       element = EL_STEEL_CHAR_2;
6340       break;
6341
6342     case 0x166b:        // (green steel)
6343       element = EL_STEEL_CHAR_3;
6344       break;
6345
6346     case 0x166c:        // (green steel)
6347       element = EL_STEEL_CHAR_4;
6348       break;
6349
6350     case 0x166d:        // (green steel)
6351       element = EL_STEEL_CHAR_5;
6352       break;
6353
6354     case 0x166e:        // (green steel)
6355       element = EL_STEEL_CHAR_6;
6356       break;
6357
6358     case 0x166f:        // (green steel)
6359       element = EL_STEEL_CHAR_7;
6360       break;
6361
6362     case 0x1670:        // (green steel)
6363       element = EL_STEEL_CHAR_8;
6364       break;
6365
6366     case 0x1671:        // (green steel)
6367       element = EL_STEEL_CHAR_9;
6368       break;
6369
6370     case 0x1672:        // (green steel)
6371       element = EL_STEEL_CHAR_PERIOD;
6372       break;
6373
6374     case 0x1673:        // (green steel)
6375       element = EL_STEEL_CHAR_EXCLAM;
6376       break;
6377
6378     case 0x1674:        // (green steel)
6379       element = EL_STEEL_CHAR_COLON;
6380       break;
6381
6382     case 0x1675:        // (green steel)
6383       element = EL_STEEL_CHAR_LESS;
6384       break;
6385
6386     case 0x1676:        // (green steel)
6387       element = EL_STEEL_CHAR_GREATER;
6388       break;
6389
6390     case 0x1677:        // (green steel)
6391       element = EL_STEEL_CHAR_QUESTION;
6392       break;
6393
6394     case 0x1678:        // (green steel)
6395       element = EL_STEEL_CHAR_COPYRIGHT;
6396       break;
6397
6398     case 0x1679:        // (green steel)
6399       element = EL_STEEL_CHAR_UP;
6400       break;
6401
6402     case 0x167a:        // (green steel)
6403       element = EL_STEEL_CHAR_DOWN;
6404       break;
6405
6406     case 0x167b:        // (green steel)
6407       element = EL_STEEL_CHAR_BUTTON;
6408       break;
6409
6410     case 0x167c:        // (green steel)
6411       element = EL_STEEL_CHAR_PLUS;
6412       break;
6413
6414     case 0x167d:        // (green steel)
6415       element = EL_STEEL_CHAR_MINUS;
6416       break;
6417
6418     case 0x167e:        // (green steel)
6419       element = EL_STEEL_CHAR_APOSTROPHE;
6420       break;
6421
6422     case 0x167f:        // (green steel)
6423       element = EL_STEEL_CHAR_PARENLEFT;
6424       break;
6425
6426     case 0x1680:        // (green steel)
6427       element = EL_STEEL_CHAR_PARENRIGHT;
6428       break;
6429
6430     case 0x1681:        // gate (red)
6431       element = EL_EM_GATE_1;
6432       break;
6433
6434     case 0x1682:        // secret gate (red)
6435       element = EL_EM_GATE_1_GRAY;
6436       break;
6437
6438     case 0x1683:        // gate (yellow)
6439       element = EL_EM_GATE_2;
6440       break;
6441
6442     case 0x1684:        // secret gate (yellow)
6443       element = EL_EM_GATE_2_GRAY;
6444       break;
6445
6446     case 0x1685:        // gate (blue)
6447       element = EL_EM_GATE_4;
6448       break;
6449
6450     case 0x1686:        // secret gate (blue)
6451       element = EL_EM_GATE_4_GRAY;
6452       break;
6453
6454     case 0x1687:        // gate (green)
6455       element = EL_EM_GATE_3;
6456       break;
6457
6458     case 0x1688:        // secret gate (green)
6459       element = EL_EM_GATE_3_GRAY;
6460       break;
6461
6462     case 0x1689:        // gate (white)
6463       element = EL_DC_GATE_WHITE;
6464       break;
6465
6466     case 0x168a:        // secret gate (white)
6467       element = EL_DC_GATE_WHITE_GRAY;
6468       break;
6469
6470     case 0x168b:        // secret gate (no key)
6471       element = EL_DC_GATE_FAKE_GRAY;
6472       break;
6473
6474     case 0x168c:
6475       element = EL_ROBOT_WHEEL;
6476       break;
6477
6478     case 0x168d:
6479       element = EL_DC_TIMEGATE_SWITCH;
6480       break;
6481
6482     case 0x168e:
6483       element = EL_ACID_POOL_BOTTOM;
6484       break;
6485
6486     case 0x168f:
6487       element = EL_ACID_POOL_TOPLEFT;
6488       break;
6489
6490     case 0x1690:
6491       element = EL_ACID_POOL_TOPRIGHT;
6492       break;
6493
6494     case 0x1691:
6495       element = EL_ACID_POOL_BOTTOMLEFT;
6496       break;
6497
6498     case 0x1692:
6499       element = EL_ACID_POOL_BOTTOMRIGHT;
6500       break;
6501
6502     case 0x1693:
6503       element = EL_STEELWALL;
6504       break;
6505
6506     case 0x1694:
6507       element = EL_STEELWALL_SLIPPERY;
6508       break;
6509
6510     case 0x1695:        // steel wall (not round)
6511       element = EL_STEELWALL;
6512       break;
6513
6514     case 0x1696:        // steel wall (left)
6515       element = EL_DC_STEELWALL_1_LEFT;
6516       break;
6517
6518     case 0x1697:        // steel wall (bottom)
6519       element = EL_DC_STEELWALL_1_BOTTOM;
6520       break;
6521
6522     case 0x1698:        // steel wall (right)
6523       element = EL_DC_STEELWALL_1_RIGHT;
6524       break;
6525
6526     case 0x1699:        // steel wall (top)
6527       element = EL_DC_STEELWALL_1_TOP;
6528       break;
6529
6530     case 0x169a:        // steel wall (left/bottom)
6531       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6532       break;
6533
6534     case 0x169b:        // steel wall (right/bottom)
6535       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6536       break;
6537
6538     case 0x169c:        // steel wall (right/top)
6539       element = EL_DC_STEELWALL_1_TOPRIGHT;
6540       break;
6541
6542     case 0x169d:        // steel wall (left/top)
6543       element = EL_DC_STEELWALL_1_TOPLEFT;
6544       break;
6545
6546     case 0x169e:        // steel wall (right/bottom small)
6547       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6548       break;
6549
6550     case 0x169f:        // steel wall (left/bottom small)
6551       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6552       break;
6553
6554     case 0x16a0:        // steel wall (right/top small)
6555       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6556       break;
6557
6558     case 0x16a1:        // steel wall (left/top small)
6559       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6560       break;
6561
6562     case 0x16a2:        // steel wall (left/right)
6563       element = EL_DC_STEELWALL_1_VERTICAL;
6564       break;
6565
6566     case 0x16a3:        // steel wall (top/bottom)
6567       element = EL_DC_STEELWALL_1_HORIZONTAL;
6568       break;
6569
6570     case 0x16a4:        // steel wall 2 (left end)
6571       element = EL_DC_STEELWALL_2_LEFT;
6572       break;
6573
6574     case 0x16a5:        // steel wall 2 (right end)
6575       element = EL_DC_STEELWALL_2_RIGHT;
6576       break;
6577
6578     case 0x16a6:        // steel wall 2 (top end)
6579       element = EL_DC_STEELWALL_2_TOP;
6580       break;
6581
6582     case 0x16a7:        // steel wall 2 (bottom end)
6583       element = EL_DC_STEELWALL_2_BOTTOM;
6584       break;
6585
6586     case 0x16a8:        // steel wall 2 (left/right)
6587       element = EL_DC_STEELWALL_2_HORIZONTAL;
6588       break;
6589
6590     case 0x16a9:        // steel wall 2 (up/down)
6591       element = EL_DC_STEELWALL_2_VERTICAL;
6592       break;
6593
6594     case 0x16aa:        // steel wall 2 (mid)
6595       element = EL_DC_STEELWALL_2_MIDDLE;
6596       break;
6597
6598     case 0x16ab:
6599       element = EL_SIGN_EXCLAMATION;
6600       break;
6601
6602     case 0x16ac:
6603       element = EL_SIGN_RADIOACTIVITY;
6604       break;
6605
6606     case 0x16ad:
6607       element = EL_SIGN_STOP;
6608       break;
6609
6610     case 0x16ae:
6611       element = EL_SIGN_WHEELCHAIR;
6612       break;
6613
6614     case 0x16af:
6615       element = EL_SIGN_PARKING;
6616       break;
6617
6618     case 0x16b0:
6619       element = EL_SIGN_NO_ENTRY;
6620       break;
6621
6622     case 0x16b1:
6623       element = EL_SIGN_HEART;
6624       break;
6625
6626     case 0x16b2:
6627       element = EL_SIGN_GIVE_WAY;
6628       break;
6629
6630     case 0x16b3:
6631       element = EL_SIGN_ENTRY_FORBIDDEN;
6632       break;
6633
6634     case 0x16b4:
6635       element = EL_SIGN_EMERGENCY_EXIT;
6636       break;
6637
6638     case 0x16b5:
6639       element = EL_SIGN_YIN_YANG;
6640       break;
6641
6642     case 0x16b6:
6643       element = EL_WALL_EMERALD;
6644       break;
6645
6646     case 0x16b7:
6647       element = EL_WALL_DIAMOND;
6648       break;
6649
6650     case 0x16b8:
6651       element = EL_WALL_PEARL;
6652       break;
6653
6654     case 0x16b9:
6655       element = EL_WALL_CRYSTAL;
6656       break;
6657
6658     case 0x16ba:
6659       element = EL_INVISIBLE_WALL;
6660       break;
6661
6662     case 0x16bb:
6663       element = EL_INVISIBLE_STEELWALL;
6664       break;
6665
6666       // 0x16bc - 0x16cb:
6667       // EL_INVISIBLE_SAND
6668
6669     case 0x16cc:
6670       element = EL_LIGHT_SWITCH;
6671       break;
6672
6673     case 0x16cd:
6674       element = EL_ENVELOPE_1;
6675       break;
6676
6677     default:
6678       if (element >= 0x0117 && element <= 0x036e)       // (?)
6679         element = EL_DIAMOND;
6680       else if (element >= 0x042d && element <= 0x0684)  // (?)
6681         element = EL_EMERALD;
6682       else if (element >= 0x157c && element <= 0x158b)
6683         element = EL_SAND;
6684       else if (element >= 0x1590 && element <= 0x159f)
6685         element = EL_DC_LANDMINE;
6686       else if (element >= 0x16bc && element <= 0x16cb)
6687         element = EL_INVISIBLE_SAND;
6688       else
6689       {
6690         Warn("unknown Diamond Caves element 0x%04x", element);
6691
6692         element = EL_UNKNOWN;
6693       }
6694       break;
6695   }
6696
6697   return getMappedElement(element);
6698 }
6699
6700 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6701 {
6702   byte header[DC_LEVEL_HEADER_SIZE];
6703   int envelope_size;
6704   int envelope_header_pos = 62;
6705   int envelope_content_pos = 94;
6706   int level_name_pos = 251;
6707   int level_author_pos = 292;
6708   int envelope_header_len;
6709   int envelope_content_len;
6710   int level_name_len;
6711   int level_author_len;
6712   int fieldx, fieldy;
6713   int num_yamyam_contents;
6714   int i, x, y;
6715
6716   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6717
6718   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6719   {
6720     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6721
6722     header[i * 2 + 0] = header_word >> 8;
6723     header[i * 2 + 1] = header_word & 0xff;
6724   }
6725
6726   // read some values from level header to check level decoding integrity
6727   fieldx = header[6] | (header[7] << 8);
6728   fieldy = header[8] | (header[9] << 8);
6729   num_yamyam_contents = header[60] | (header[61] << 8);
6730
6731   // do some simple sanity checks to ensure that level was correctly decoded
6732   if (fieldx < 1 || fieldx > 256 ||
6733       fieldy < 1 || fieldy > 256 ||
6734       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6735   {
6736     level->no_valid_file = TRUE;
6737
6738     Warn("cannot decode level from stream -- using empty level");
6739
6740     return;
6741   }
6742
6743   // maximum envelope header size is 31 bytes
6744   envelope_header_len   = header[envelope_header_pos];
6745   // maximum envelope content size is 110 (156?) bytes
6746   envelope_content_len  = header[envelope_content_pos];
6747
6748   // maximum level title size is 40 bytes
6749   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6750   // maximum level author size is 30 (51?) bytes
6751   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6752
6753   envelope_size = 0;
6754
6755   for (i = 0; i < envelope_header_len; i++)
6756     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6757       level->envelope[0].text[envelope_size++] =
6758         header[envelope_header_pos + 1 + i];
6759
6760   if (envelope_header_len > 0 && envelope_content_len > 0)
6761   {
6762     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6763       level->envelope[0].text[envelope_size++] = '\n';
6764     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6765       level->envelope[0].text[envelope_size++] = '\n';
6766   }
6767
6768   for (i = 0; i < envelope_content_len; i++)
6769     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6770       level->envelope[0].text[envelope_size++] =
6771         header[envelope_content_pos + 1 + i];
6772
6773   level->envelope[0].text[envelope_size] = '\0';
6774
6775   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6776   level->envelope[0].ysize = 10;
6777   level->envelope[0].autowrap = TRUE;
6778   level->envelope[0].centered = TRUE;
6779
6780   for (i = 0; i < level_name_len; i++)
6781     level->name[i] = header[level_name_pos + 1 + i];
6782   level->name[level_name_len] = '\0';
6783
6784   for (i = 0; i < level_author_len; i++)
6785     level->author[i] = header[level_author_pos + 1 + i];
6786   level->author[level_author_len] = '\0';
6787
6788   num_yamyam_contents = header[60] | (header[61] << 8);
6789   level->num_yamyam_contents =
6790     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6791
6792   for (i = 0; i < num_yamyam_contents; i++)
6793   {
6794     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6795     {
6796       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6797       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6798
6799       if (i < MAX_ELEMENT_CONTENTS)
6800         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6801     }
6802   }
6803
6804   fieldx = header[6] | (header[7] << 8);
6805   fieldy = header[8] | (header[9] << 8);
6806   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6807   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6808
6809   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6810   {
6811     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6812     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6813
6814     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6815       level->field[x][y] = getMappedElement_DC(element_dc);
6816   }
6817
6818   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6819   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6820   level->field[x][y] = EL_PLAYER_1;
6821
6822   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6823   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6824   level->field[x][y] = EL_PLAYER_2;
6825
6826   level->gems_needed            = header[18] | (header[19] << 8);
6827
6828   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6829   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6830   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6831   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6832   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6833   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6834   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6835   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6836   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6837   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6838   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6839   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6840
6841   level->time                   = header[44] | (header[45] << 8);
6842
6843   level->amoeba_speed           = header[46] | (header[47] << 8);
6844   level->time_light             = header[48] | (header[49] << 8);
6845   level->time_timegate          = header[50] | (header[51] << 8);
6846   level->time_wheel             = header[52] | (header[53] << 8);
6847   level->time_magic_wall        = header[54] | (header[55] << 8);
6848   level->extra_time             = header[56] | (header[57] << 8);
6849   level->shield_normal_time     = header[58] | (header[59] << 8);
6850
6851   // shield and extra time elements do not have a score
6852   level->score[SC_SHIELD]       = 0;
6853   level->extra_time_score       = 0;
6854
6855   // set time for normal and deadly shields to the same value
6856   level->shield_deadly_time     = level->shield_normal_time;
6857
6858   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6859   // can slip down from flat walls, like normal walls and steel walls
6860   level->em_slippery_gems = TRUE;
6861
6862   // time score is counted for each 10 seconds left in Diamond Caves levels
6863   level->time_score_base = 10;
6864 }
6865
6866 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6867                                      struct LevelFileInfo *level_file_info,
6868                                      boolean level_info_only)
6869 {
6870   char *filename = level_file_info->filename;
6871   File *file;
6872   int num_magic_bytes = 8;
6873   char magic_bytes[num_magic_bytes + 1];
6874   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6875
6876   if (!(file = openFile(filename, MODE_READ)))
6877   {
6878     level->no_valid_file = TRUE;
6879
6880     if (!level_info_only)
6881       Warn("cannot read level '%s' -- using empty level", filename);
6882
6883     return;
6884   }
6885
6886   // fseek(file, 0x0000, SEEK_SET);
6887
6888   if (level_file_info->packed)
6889   {
6890     // read "magic bytes" from start of file
6891     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6892       magic_bytes[0] = '\0';
6893
6894     // check "magic bytes" for correct file format
6895     if (!strPrefix(magic_bytes, "DC2"))
6896     {
6897       level->no_valid_file = TRUE;
6898
6899       Warn("unknown DC level file '%s' -- using empty level", filename);
6900
6901       return;
6902     }
6903
6904     if (strPrefix(magic_bytes, "DC2Win95") ||
6905         strPrefix(magic_bytes, "DC2Win98"))
6906     {
6907       int position_first_level = 0x00fa;
6908       int extra_bytes = 4;
6909       int skip_bytes;
6910
6911       // advance file stream to first level inside the level package
6912       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6913
6914       // each block of level data is followed by block of non-level data
6915       num_levels_to_skip *= 2;
6916
6917       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6918       while (num_levels_to_skip >= 0)
6919       {
6920         // advance file stream to next level inside the level package
6921         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6922         {
6923           level->no_valid_file = TRUE;
6924
6925           Warn("cannot fseek in file '%s' -- using empty level", filename);
6926
6927           return;
6928         }
6929
6930         // skip apparently unused extra bytes following each level
6931         ReadUnusedBytesFromFile(file, extra_bytes);
6932
6933         // read size of next level in level package
6934         skip_bytes = getFile32BitLE(file);
6935
6936         num_levels_to_skip--;
6937       }
6938     }
6939     else
6940     {
6941       level->no_valid_file = TRUE;
6942
6943       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6944
6945       return;
6946     }
6947   }
6948
6949   LoadLevelFromFileStream_DC(file, level);
6950
6951   closeFile(file);
6952 }
6953
6954
6955 // ----------------------------------------------------------------------------
6956 // functions for loading SB level
6957 // ----------------------------------------------------------------------------
6958
6959 int getMappedElement_SB(int element_ascii, boolean use_ces)
6960 {
6961   static struct
6962   {
6963     int ascii;
6964     int sb;
6965     int ce;
6966   }
6967   sb_element_mapping[] =
6968   {
6969     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6970     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6971     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6972     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6973     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6974     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6975     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6976     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6977
6978     { 0,   -1,                      -1          },
6979   };
6980
6981   int i;
6982
6983   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6984     if (element_ascii == sb_element_mapping[i].ascii)
6985       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6986
6987   return EL_UNDEFINED;
6988 }
6989
6990 static void SetLevelSettings_SB(struct LevelInfo *level)
6991 {
6992   // time settings
6993   level->time = 0;
6994   level->use_step_counter = TRUE;
6995
6996   // score settings
6997   level->score[SC_TIME_BONUS] = 0;
6998   level->time_score_base = 1;
6999   level->rate_time_over_score = TRUE;
7000
7001   // game settings
7002   level->auto_exit_sokoban = TRUE;
7003 }
7004
7005 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7006                                      struct LevelFileInfo *level_file_info,
7007                                      boolean level_info_only)
7008 {
7009   char *filename = level_file_info->filename;
7010   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7011   char last_comment[MAX_LINE_LEN];
7012   char level_name[MAX_LINE_LEN];
7013   char *line_ptr;
7014   File *file;
7015   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7016   boolean read_continued_line = FALSE;
7017   boolean reading_playfield = FALSE;
7018   boolean got_valid_playfield_line = FALSE;
7019   boolean invalid_playfield_char = FALSE;
7020   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7021   int file_level_nr = 0;
7022   int x = 0, y = 0;             // initialized to make compilers happy
7023
7024   last_comment[0] = '\0';
7025   level_name[0] = '\0';
7026
7027   if (!(file = openFile(filename, MODE_READ)))
7028   {
7029     level->no_valid_file = TRUE;
7030
7031     if (!level_info_only)
7032       Warn("cannot read level '%s' -- using empty level", filename);
7033
7034     return;
7035   }
7036
7037   while (!checkEndOfFile(file))
7038   {
7039     // level successfully read, but next level may follow here
7040     if (!got_valid_playfield_line && reading_playfield)
7041     {
7042       // read playfield from single level file -- skip remaining file
7043       if (!level_file_info->packed)
7044         break;
7045
7046       if (file_level_nr >= num_levels_to_skip)
7047         break;
7048
7049       file_level_nr++;
7050
7051       last_comment[0] = '\0';
7052       level_name[0] = '\0';
7053
7054       reading_playfield = FALSE;
7055     }
7056
7057     got_valid_playfield_line = FALSE;
7058
7059     // read next line of input file
7060     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7061       break;
7062
7063     // cut trailing line break (this can be newline and/or carriage return)
7064     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7065       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7066         *line_ptr = '\0';
7067
7068     // copy raw input line for later use (mainly debugging output)
7069     strcpy(line_raw, line);
7070
7071     if (read_continued_line)
7072     {
7073       // append new line to existing line, if there is enough space
7074       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7075         strcat(previous_line, line_ptr);
7076
7077       strcpy(line, previous_line);      // copy storage buffer to line
7078
7079       read_continued_line = FALSE;
7080     }
7081
7082     // if the last character is '\', continue at next line
7083     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7084     {
7085       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7086       strcpy(previous_line, line);      // copy line to storage buffer
7087
7088       read_continued_line = TRUE;
7089
7090       continue;
7091     }
7092
7093     // skip empty lines
7094     if (line[0] == '\0')
7095       continue;
7096
7097     // extract comment text from comment line
7098     if (line[0] == ';')
7099     {
7100       for (line_ptr = line; *line_ptr; line_ptr++)
7101         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7102           break;
7103
7104       strcpy(last_comment, line_ptr);
7105
7106       continue;
7107     }
7108
7109     // extract level title text from line containing level title
7110     if (line[0] == '\'')
7111     {
7112       strcpy(level_name, &line[1]);
7113
7114       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7115         level_name[strlen(level_name) - 1] = '\0';
7116
7117       continue;
7118     }
7119
7120     // skip lines containing only spaces (or empty lines)
7121     for (line_ptr = line; *line_ptr; line_ptr++)
7122       if (*line_ptr != ' ')
7123         break;
7124     if (*line_ptr == '\0')
7125       continue;
7126
7127     // at this point, we have found a line containing part of a playfield
7128
7129     got_valid_playfield_line = TRUE;
7130
7131     if (!reading_playfield)
7132     {
7133       reading_playfield = TRUE;
7134       invalid_playfield_char = FALSE;
7135
7136       for (x = 0; x < MAX_LEV_FIELDX; x++)
7137         for (y = 0; y < MAX_LEV_FIELDY; y++)
7138           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7139
7140       level->fieldx = 0;
7141       level->fieldy = 0;
7142
7143       // start with topmost tile row
7144       y = 0;
7145     }
7146
7147     // skip playfield line if larger row than allowed
7148     if (y >= MAX_LEV_FIELDY)
7149       continue;
7150
7151     // start with leftmost tile column
7152     x = 0;
7153
7154     // read playfield elements from line
7155     for (line_ptr = line; *line_ptr; line_ptr++)
7156     {
7157       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7158
7159       // stop parsing playfield line if larger column than allowed
7160       if (x >= MAX_LEV_FIELDX)
7161         break;
7162
7163       if (mapped_sb_element == EL_UNDEFINED)
7164       {
7165         invalid_playfield_char = TRUE;
7166
7167         break;
7168       }
7169
7170       level->field[x][y] = mapped_sb_element;
7171
7172       // continue with next tile column
7173       x++;
7174
7175       level->fieldx = MAX(x, level->fieldx);
7176     }
7177
7178     if (invalid_playfield_char)
7179     {
7180       // if first playfield line, treat invalid lines as comment lines
7181       if (y == 0)
7182         reading_playfield = FALSE;
7183
7184       continue;
7185     }
7186
7187     // continue with next tile row
7188     y++;
7189   }
7190
7191   closeFile(file);
7192
7193   level->fieldy = y;
7194
7195   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7196   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7197
7198   if (!reading_playfield)
7199   {
7200     level->no_valid_file = TRUE;
7201
7202     Warn("cannot read level '%s' -- using empty level", filename);
7203
7204     return;
7205   }
7206
7207   if (*level_name != '\0')
7208   {
7209     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7210     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7211   }
7212   else if (*last_comment != '\0')
7213   {
7214     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7215     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7216   }
7217   else
7218   {
7219     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7220   }
7221
7222   // set all empty fields beyond the border walls to invisible steel wall
7223   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7224   {
7225     if ((x == 0 || x == level->fieldx - 1 ||
7226          y == 0 || y == level->fieldy - 1) &&
7227         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7228       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7229                      level->field, level->fieldx, level->fieldy);
7230   }
7231
7232   // set special level settings for Sokoban levels
7233   SetLevelSettings_SB(level);
7234
7235   if (load_xsb_to_ces)
7236   {
7237     // special global settings can now be set in level template
7238     level->use_custom_template = TRUE;
7239   }
7240 }
7241
7242
7243 // -------------------------------------------------------------------------
7244 // functions for handling native levels
7245 // -------------------------------------------------------------------------
7246
7247 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7248                                      struct LevelFileInfo *level_file_info,
7249                                      boolean level_info_only)
7250 {
7251   int pos = 0;
7252
7253   // determine position of requested level inside level package
7254   if (level_file_info->packed)
7255     pos = level_file_info->nr - leveldir_current->first_level;
7256
7257   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7258     level->no_valid_file = TRUE;
7259 }
7260
7261 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7262                                      struct LevelFileInfo *level_file_info,
7263                                      boolean level_info_only)
7264 {
7265   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7266     level->no_valid_file = TRUE;
7267 }
7268
7269 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7270                                      struct LevelFileInfo *level_file_info,
7271                                      boolean level_info_only)
7272 {
7273   int pos = 0;
7274
7275   // determine position of requested level inside level package
7276   if (level_file_info->packed)
7277     pos = level_file_info->nr - leveldir_current->first_level;
7278
7279   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7280     level->no_valid_file = TRUE;
7281 }
7282
7283 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7284                                      struct LevelFileInfo *level_file_info,
7285                                      boolean level_info_only)
7286 {
7287   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7288     level->no_valid_file = TRUE;
7289 }
7290
7291 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7292 {
7293   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7294     CopyNativeLevel_RND_to_BD(level);
7295   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7296     CopyNativeLevel_RND_to_EM(level);
7297   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7298     CopyNativeLevel_RND_to_SP(level);
7299   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7300     CopyNativeLevel_RND_to_MM(level);
7301 }
7302
7303 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7304 {
7305   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7306     CopyNativeLevel_BD_to_RND(level);
7307   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7308     CopyNativeLevel_EM_to_RND(level);
7309   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7310     CopyNativeLevel_SP_to_RND(level);
7311   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7312     CopyNativeLevel_MM_to_RND(level);
7313 }
7314
7315 void SaveNativeLevel(struct LevelInfo *level)
7316 {
7317   // saving native level files only supported for some game engines
7318   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7319       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7320     return;
7321
7322   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7323                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7324   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7325   char *filename = getLevelFilenameFromBasename(basename);
7326
7327   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7328     return;
7329
7330   boolean success = FALSE;
7331
7332   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7333   {
7334     CopyNativeLevel_RND_to_BD(level);
7335     // CopyNativeTape_RND_to_BD(level);
7336
7337     success = SaveNativeLevel_BD(filename);
7338   }
7339   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7340   {
7341     CopyNativeLevel_RND_to_SP(level);
7342     CopyNativeTape_RND_to_SP(level);
7343
7344     success = SaveNativeLevel_SP(filename);
7345   }
7346
7347   if (success)
7348     Request("Native level file saved!", REQ_CONFIRM);
7349   else
7350     Request("Failed to save native level file!", REQ_CONFIRM);
7351 }
7352
7353
7354 // ----------------------------------------------------------------------------
7355 // functions for loading generic level
7356 // ----------------------------------------------------------------------------
7357
7358 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7359                                   struct LevelFileInfo *level_file_info,
7360                                   boolean level_info_only)
7361 {
7362   // always start with reliable default values
7363   setLevelInfoToDefaults(level, level_info_only, TRUE);
7364
7365   switch (level_file_info->type)
7366   {
7367     case LEVEL_FILE_TYPE_RND:
7368       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7369       break;
7370
7371     case LEVEL_FILE_TYPE_BD:
7372       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7373       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7374       break;
7375
7376     case LEVEL_FILE_TYPE_EM:
7377       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7378       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7379       break;
7380
7381     case LEVEL_FILE_TYPE_SP:
7382       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7383       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7384       break;
7385
7386     case LEVEL_FILE_TYPE_MM:
7387       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7388       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7389       break;
7390
7391     case LEVEL_FILE_TYPE_DC:
7392       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7393       break;
7394
7395     case LEVEL_FILE_TYPE_SB:
7396       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7397       break;
7398
7399     default:
7400       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7401       break;
7402   }
7403
7404   // if level file is invalid, restore level structure to default values
7405   if (level->no_valid_file)
7406     setLevelInfoToDefaults(level, level_info_only, FALSE);
7407
7408   if (check_special_flags("use_native_bd_game_engine"))
7409     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7410
7411   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7412     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7413
7414   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7415     CopyNativeLevel_Native_to_RND(level);
7416 }
7417
7418 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7419 {
7420   static struct LevelFileInfo level_file_info;
7421
7422   // always start with reliable default values
7423   setFileInfoToDefaults(&level_file_info);
7424
7425   level_file_info.nr = 0;                       // unknown level number
7426   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7427
7428   setString(&level_file_info.filename, filename);
7429
7430   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7431 }
7432
7433 static void LoadLevel_InitVersion(struct LevelInfo *level)
7434 {
7435   int i, j;
7436
7437   if (leveldir_current == NULL)         // only when dumping level
7438     return;
7439
7440   // all engine modifications also valid for levels which use latest engine
7441   if (level->game_version < VERSION_IDENT(3,2,0,5))
7442   {
7443     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7444     level->time_score_base = 10;
7445   }
7446
7447   if (leveldir_current->latest_engine)
7448   {
7449     // ---------- use latest game engine --------------------------------------
7450
7451     /* For all levels which are forced to use the latest game engine version
7452        (normally all but user contributed, private and undefined levels), set
7453        the game engine version to the actual version; this allows for actual
7454        corrections in the game engine to take effect for existing, converted
7455        levels (from "classic" or other existing games) to make the emulation
7456        of the corresponding game more accurate, while (hopefully) not breaking
7457        existing levels created from other players. */
7458
7459     level->game_version = GAME_VERSION_ACTUAL;
7460
7461     /* Set special EM style gems behaviour: EM style gems slip down from
7462        normal, steel and growing wall. As this is a more fundamental change,
7463        it seems better to set the default behaviour to "off" (as it is more
7464        natural) and make it configurable in the level editor (as a property
7465        of gem style elements). Already existing converted levels (neither
7466        private nor contributed levels) are changed to the new behaviour. */
7467
7468     if (level->file_version < FILE_VERSION_2_0)
7469       level->em_slippery_gems = TRUE;
7470
7471     return;
7472   }
7473
7474   // ---------- use game engine the level was created with --------------------
7475
7476   /* For all levels which are not forced to use the latest game engine
7477      version (normally user contributed, private and undefined levels),
7478      use the version of the game engine the levels were created for.
7479
7480      Since 2.0.1, the game engine version is now directly stored
7481      in the level file (chunk "VERS"), so there is no need anymore
7482      to set the game version from the file version (except for old,
7483      pre-2.0 levels, where the game version is still taken from the
7484      file format version used to store the level -- see above). */
7485
7486   // player was faster than enemies in 1.0.0 and before
7487   if (level->file_version == FILE_VERSION_1_0)
7488     for (i = 0; i < MAX_PLAYERS; i++)
7489       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7490
7491   // default behaviour for EM style gems was "slippery" only in 2.0.1
7492   if (level->game_version == VERSION_IDENT(2,0,1,0))
7493     level->em_slippery_gems = TRUE;
7494
7495   // springs could be pushed over pits before (pre-release version) 2.2.0
7496   if (level->game_version < VERSION_IDENT(2,2,0,0))
7497     level->use_spring_bug = TRUE;
7498
7499   if (level->game_version < VERSION_IDENT(3,2,0,5))
7500   {
7501     // time orb caused limited time in endless time levels before 3.2.0-5
7502     level->use_time_orb_bug = TRUE;
7503
7504     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7505     level->block_snap_field = FALSE;
7506
7507     // extra time score was same value as time left score before 3.2.0-5
7508     level->extra_time_score = level->score[SC_TIME_BONUS];
7509   }
7510
7511   if (level->game_version < VERSION_IDENT(3,2,0,7))
7512   {
7513     // default behaviour for snapping was "not continuous" before 3.2.0-7
7514     level->continuous_snapping = FALSE;
7515   }
7516
7517   // only few elements were able to actively move into acid before 3.1.0
7518   // trigger settings did not exist before 3.1.0; set to default "any"
7519   if (level->game_version < VERSION_IDENT(3,1,0,0))
7520   {
7521     // correct "can move into acid" settings (all zero in old levels)
7522
7523     level->can_move_into_acid_bits = 0; // nothing can move into acid
7524     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7525
7526     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7527     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7528     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7529     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7530
7531     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7532       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7533
7534     // correct trigger settings (stored as zero == "none" in old levels)
7535
7536     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7537     {
7538       int element = EL_CUSTOM_START + i;
7539       struct ElementInfo *ei = &element_info[element];
7540
7541       for (j = 0; j < ei->num_change_pages; j++)
7542       {
7543         struct ElementChangeInfo *change = &ei->change_page[j];
7544
7545         change->trigger_player = CH_PLAYER_ANY;
7546         change->trigger_page = CH_PAGE_ANY;
7547       }
7548     }
7549   }
7550
7551   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7552   {
7553     int element = EL_CUSTOM_256;
7554     struct ElementInfo *ei = &element_info[element];
7555     struct ElementChangeInfo *change = &ei->change_page[0];
7556
7557     /* This is needed to fix a problem that was caused by a bugfix in function
7558        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7559        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7560        not replace walkable elements, but instead just placed the player on it,
7561        without placing the Sokoban field under the player). Unfortunately, this
7562        breaks "Snake Bite" style levels when the snake is halfway through a door
7563        that just closes (the snake head is still alive and can be moved in this
7564        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7565        player (without Sokoban element) which then gets killed as designed). */
7566
7567     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7568          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7569         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7570       change->target_element = EL_PLAYER_1;
7571   }
7572
7573   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7574   if (level->game_version < VERSION_IDENT(3,2,5,0))
7575   {
7576     /* This is needed to fix a problem that was caused by a bugfix in function
7577        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7578        corrects the behaviour when a custom element changes to another custom
7579        element with a higher element number that has change actions defined.
7580        Normally, only one change per frame is allowed for custom elements.
7581        Therefore, it is checked if a custom element already changed in the
7582        current frame; if it did, subsequent changes are suppressed.
7583        Unfortunately, this is only checked for element changes, but not for
7584        change actions, which are still executed. As the function above loops
7585        through all custom elements from lower to higher, an element change
7586        resulting in a lower CE number won't be checked again, while a target
7587        element with a higher number will also be checked, and potential change
7588        actions will get executed for this CE, too (which is wrong), while
7589        further changes are ignored (which is correct). As this bugfix breaks
7590        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7591        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7592        behaviour for existing levels and tapes that make use of this bug */
7593
7594     level->use_action_after_change_bug = TRUE;
7595   }
7596
7597   // not centering level after relocating player was default only in 3.2.3
7598   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7599     level->shifted_relocation = TRUE;
7600
7601   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7602   if (level->game_version < VERSION_IDENT(3,2,6,0))
7603     level->em_explodes_by_fire = TRUE;
7604
7605   // levels were solved by the first player entering an exit up to 4.1.0.0
7606   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7607     level->solved_by_one_player = TRUE;
7608
7609   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7610   if (level->game_version < VERSION_IDENT(4,1,1,1))
7611     level->use_life_bugs = TRUE;
7612
7613   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7614   if (level->game_version < VERSION_IDENT(4,1,1,1))
7615     level->sb_objects_needed = FALSE;
7616
7617   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7618   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7619     level->finish_dig_collect = FALSE;
7620
7621   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7622   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7623     level->keep_walkable_ce = TRUE;
7624 }
7625
7626 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7627 {
7628   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7629   int x, y;
7630
7631   // check if this level is (not) a Sokoban level
7632   for (y = 0; y < level->fieldy; y++)
7633     for (x = 0; x < level->fieldx; x++)
7634       if (!IS_SB_ELEMENT(Tile[x][y]))
7635         is_sokoban_level = FALSE;
7636
7637   if (is_sokoban_level)
7638   {
7639     // set special level settings for Sokoban levels
7640     SetLevelSettings_SB(level);
7641   }
7642 }
7643
7644 static void LoadLevel_InitSettings(struct LevelInfo *level)
7645 {
7646   // adjust level settings for (non-native) Sokoban-style levels
7647   LoadLevel_InitSettings_SB(level);
7648
7649   // rename levels with title "nameless level" or if renaming is forced
7650   if (leveldir_current->empty_level_name != NULL &&
7651       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7652        leveldir_current->force_level_name))
7653     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7654              leveldir_current->empty_level_name, level_nr);
7655 }
7656
7657 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7658 {
7659   int i, x, y;
7660
7661   // map elements that have changed in newer versions
7662   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7663                                                     level->game_version);
7664   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7665     for (x = 0; x < 3; x++)
7666       for (y = 0; y < 3; y++)
7667         level->yamyam_content[i].e[x][y] =
7668           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7669                                     level->game_version);
7670
7671 }
7672
7673 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7674 {
7675   int i, j;
7676
7677   // map custom element change events that have changed in newer versions
7678   // (these following values were accidentally changed in version 3.0.1)
7679   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7680   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7681   {
7682     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7683     {
7684       int element = EL_CUSTOM_START + i;
7685
7686       // order of checking and copying events to be mapped is important
7687       // (do not change the start and end value -- they are constant)
7688       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7689       {
7690         if (HAS_CHANGE_EVENT(element, j - 2))
7691         {
7692           SET_CHANGE_EVENT(element, j - 2, FALSE);
7693           SET_CHANGE_EVENT(element, j, TRUE);
7694         }
7695       }
7696
7697       // order of checking and copying events to be mapped is important
7698       // (do not change the start and end value -- they are constant)
7699       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7700       {
7701         if (HAS_CHANGE_EVENT(element, j - 1))
7702         {
7703           SET_CHANGE_EVENT(element, j - 1, FALSE);
7704           SET_CHANGE_EVENT(element, j, TRUE);
7705         }
7706       }
7707     }
7708   }
7709
7710   // initialize "can_change" field for old levels with only one change page
7711   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7712   {
7713     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7714     {
7715       int element = EL_CUSTOM_START + i;
7716
7717       if (CAN_CHANGE(element))
7718         element_info[element].change->can_change = TRUE;
7719     }
7720   }
7721
7722   // correct custom element values (for old levels without these options)
7723   if (level->game_version < VERSION_IDENT(3,1,1,0))
7724   {
7725     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7726     {
7727       int element = EL_CUSTOM_START + i;
7728       struct ElementInfo *ei = &element_info[element];
7729
7730       if (ei->access_direction == MV_NO_DIRECTION)
7731         ei->access_direction = MV_ALL_DIRECTIONS;
7732     }
7733   }
7734
7735   // correct custom element values (fix invalid values for all versions)
7736   if (1)
7737   {
7738     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7739     {
7740       int element = EL_CUSTOM_START + i;
7741       struct ElementInfo *ei = &element_info[element];
7742
7743       for (j = 0; j < ei->num_change_pages; j++)
7744       {
7745         struct ElementChangeInfo *change = &ei->change_page[j];
7746
7747         if (change->trigger_player == CH_PLAYER_NONE)
7748           change->trigger_player = CH_PLAYER_ANY;
7749
7750         if (change->trigger_side == CH_SIDE_NONE)
7751           change->trigger_side = CH_SIDE_ANY;
7752       }
7753     }
7754   }
7755
7756   // initialize "can_explode" field for old levels which did not store this
7757   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7758   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7759   {
7760     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7761     {
7762       int element = EL_CUSTOM_START + i;
7763
7764       if (EXPLODES_1X1_OLD(element))
7765         element_info[element].explosion_type = EXPLODES_1X1;
7766
7767       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7768                                              EXPLODES_SMASHED(element) ||
7769                                              EXPLODES_IMPACT(element)));
7770     }
7771   }
7772
7773   // correct previously hard-coded move delay values for maze runner style
7774   if (level->game_version < VERSION_IDENT(3,1,1,0))
7775   {
7776     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7777     {
7778       int element = EL_CUSTOM_START + i;
7779
7780       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7781       {
7782         // previously hard-coded and therefore ignored
7783         element_info[element].move_delay_fixed = 9;
7784         element_info[element].move_delay_random = 0;
7785       }
7786     }
7787   }
7788
7789   // set some other uninitialized values of custom elements in older levels
7790   if (level->game_version < VERSION_IDENT(3,1,0,0))
7791   {
7792     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7793     {
7794       int element = EL_CUSTOM_START + i;
7795
7796       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7797
7798       element_info[element].explosion_delay = 17;
7799       element_info[element].ignition_delay = 8;
7800     }
7801   }
7802
7803   // set mouse click change events to work for left/middle/right mouse button
7804   if (level->game_version < VERSION_IDENT(4,2,3,0))
7805   {
7806     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7807     {
7808       int element = EL_CUSTOM_START + i;
7809       struct ElementInfo *ei = &element_info[element];
7810
7811       for (j = 0; j < ei->num_change_pages; j++)
7812       {
7813         struct ElementChangeInfo *change = &ei->change_page[j];
7814
7815         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7816             change->has_event[CE_PRESSED_BY_MOUSE] ||
7817             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7818             change->has_event[CE_MOUSE_PRESSED_ON_X])
7819           change->trigger_side = CH_SIDE_ANY;
7820       }
7821     }
7822   }
7823 }
7824
7825 static void LoadLevel_InitElements(struct LevelInfo *level)
7826 {
7827   LoadLevel_InitStandardElements(level);
7828
7829   if (level->file_has_custom_elements)
7830     LoadLevel_InitCustomElements(level);
7831
7832   // initialize element properties for level editor etc.
7833   InitElementPropertiesEngine(level->game_version);
7834   InitElementPropertiesGfxElement();
7835 }
7836
7837 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7838 {
7839   int x, y;
7840
7841   // map elements that have changed in newer versions
7842   for (y = 0; y < level->fieldy; y++)
7843     for (x = 0; x < level->fieldx; x++)
7844       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7845                                                      level->game_version);
7846
7847   // clear unused playfield data (nicer if level gets resized in editor)
7848   for (x = 0; x < MAX_LEV_FIELDX; x++)
7849     for (y = 0; y < MAX_LEV_FIELDY; y++)
7850       if (x >= level->fieldx || y >= level->fieldy)
7851         level->field[x][y] = EL_EMPTY;
7852
7853   // copy elements to runtime playfield array
7854   for (x = 0; x < MAX_LEV_FIELDX; x++)
7855     for (y = 0; y < MAX_LEV_FIELDY; y++)
7856       Tile[x][y] = level->field[x][y];
7857
7858   // initialize level size variables for faster access
7859   lev_fieldx = level->fieldx;
7860   lev_fieldy = level->fieldy;
7861
7862   // determine border element for this level
7863   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7864     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7865   else
7866     SetBorderElement();
7867 }
7868
7869 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7870 {
7871   struct LevelFileInfo *level_file_info = &level->file_info;
7872
7873   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7874     CopyNativeLevel_RND_to_Native(level);
7875 }
7876
7877 static void LoadLevelTemplate_LoadAndInit(void)
7878 {
7879   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7880
7881   LoadLevel_InitVersion(&level_template);
7882   LoadLevel_InitElements(&level_template);
7883   LoadLevel_InitSettings(&level_template);
7884
7885   ActivateLevelTemplate();
7886 }
7887
7888 void LoadLevelTemplate(int nr)
7889 {
7890   if (!fileExists(getGlobalLevelTemplateFilename()))
7891   {
7892     Warn("no level template found for this level");
7893
7894     return;
7895   }
7896
7897   setLevelFileInfo(&level_template.file_info, nr);
7898
7899   LoadLevelTemplate_LoadAndInit();
7900 }
7901
7902 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7903 {
7904   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7905
7906   LoadLevelTemplate_LoadAndInit();
7907 }
7908
7909 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7910 {
7911   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7912
7913   if (level.use_custom_template)
7914   {
7915     if (network_level != NULL)
7916       LoadNetworkLevelTemplate(network_level);
7917     else
7918       LoadLevelTemplate(-1);
7919   }
7920
7921   LoadLevel_InitVersion(&level);
7922   LoadLevel_InitElements(&level);
7923   LoadLevel_InitPlayfield(&level);
7924   LoadLevel_InitSettings(&level);
7925
7926   LoadLevel_InitNativeEngines(&level);
7927 }
7928
7929 void LoadLevel(int nr)
7930 {
7931   SetLevelSetInfo(leveldir_current->identifier, nr);
7932
7933   setLevelFileInfo(&level.file_info, nr);
7934
7935   LoadLevel_LoadAndInit(NULL);
7936 }
7937
7938 void LoadLevelInfoOnly(int nr)
7939 {
7940   setLevelFileInfo(&level.file_info, nr);
7941
7942   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7943 }
7944
7945 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7946 {
7947   SetLevelSetInfo(network_level->leveldir_identifier,
7948                   network_level->file_info.nr);
7949
7950   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7951
7952   LoadLevel_LoadAndInit(network_level);
7953 }
7954
7955 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7956 {
7957   int chunk_size = 0;
7958
7959   chunk_size += putFileVersion(file, level->file_version);
7960   chunk_size += putFileVersion(file, level->game_version);
7961
7962   return chunk_size;
7963 }
7964
7965 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7966 {
7967   int chunk_size = 0;
7968
7969   chunk_size += putFile16BitBE(file, level->creation_date.year);
7970   chunk_size += putFile8Bit(file,    level->creation_date.month);
7971   chunk_size += putFile8Bit(file,    level->creation_date.day);
7972
7973   return chunk_size;
7974 }
7975
7976 #if ENABLE_HISTORIC_CHUNKS
7977 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7978 {
7979   int i, x, y;
7980
7981   putFile8Bit(file, level->fieldx);
7982   putFile8Bit(file, level->fieldy);
7983
7984   putFile16BitBE(file, level->time);
7985   putFile16BitBE(file, level->gems_needed);
7986
7987   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7988     putFile8Bit(file, level->name[i]);
7989
7990   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7991     putFile8Bit(file, level->score[i]);
7992
7993   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7994     for (y = 0; y < 3; y++)
7995       for (x = 0; x < 3; x++)
7996         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7997                            level->yamyam_content[i].e[x][y]));
7998   putFile8Bit(file, level->amoeba_speed);
7999   putFile8Bit(file, level->time_magic_wall);
8000   putFile8Bit(file, level->time_wheel);
8001   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8002                      level->amoeba_content));
8003   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8004   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8005   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8006   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8007
8008   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8009
8010   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8011   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8012   putFile32BitBE(file, level->can_move_into_acid_bits);
8013   putFile8Bit(file, level->dont_collide_with_bits);
8014
8015   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8016   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8017
8018   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8019   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8020   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8021
8022   putFile8Bit(file, level->game_engine_type);
8023
8024   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8025 }
8026 #endif
8027
8028 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8029 {
8030   int chunk_size = 0;
8031   int i;
8032
8033   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8034     chunk_size += putFile8Bit(file, level->name[i]);
8035
8036   return chunk_size;
8037 }
8038
8039 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8040 {
8041   int chunk_size = 0;
8042   int i;
8043
8044   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8045     chunk_size += putFile8Bit(file, level->author[i]);
8046
8047   return chunk_size;
8048 }
8049
8050 #if ENABLE_HISTORIC_CHUNKS
8051 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8052 {
8053   int chunk_size = 0;
8054   int x, y;
8055
8056   for (y = 0; y < level->fieldy; y++)
8057     for (x = 0; x < level->fieldx; x++)
8058       if (level->encoding_16bit_field)
8059         chunk_size += putFile16BitBE(file, level->field[x][y]);
8060       else
8061         chunk_size += putFile8Bit(file, level->field[x][y]);
8062
8063   return chunk_size;
8064 }
8065 #endif
8066
8067 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8068 {
8069   int chunk_size = 0;
8070   int x, y;
8071
8072   for (y = 0; y < level->fieldy; y++) 
8073     for (x = 0; x < level->fieldx; x++) 
8074       chunk_size += putFile16BitBE(file, level->field[x][y]);
8075
8076   return chunk_size;
8077 }
8078
8079 #if ENABLE_HISTORIC_CHUNKS
8080 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8081 {
8082   int i, x, y;
8083
8084   putFile8Bit(file, EL_YAMYAM);
8085   putFile8Bit(file, level->num_yamyam_contents);
8086   putFile8Bit(file, 0);
8087   putFile8Bit(file, 0);
8088
8089   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8090     for (y = 0; y < 3; y++)
8091       for (x = 0; x < 3; x++)
8092         if (level->encoding_16bit_field)
8093           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8094         else
8095           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8096 }
8097 #endif
8098
8099 #if ENABLE_HISTORIC_CHUNKS
8100 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8101 {
8102   int i, x, y;
8103   int num_contents, content_xsize, content_ysize;
8104   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8105
8106   if (element == EL_YAMYAM)
8107   {
8108     num_contents = level->num_yamyam_contents;
8109     content_xsize = 3;
8110     content_ysize = 3;
8111
8112     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8113       for (y = 0; y < 3; y++)
8114         for (x = 0; x < 3; x++)
8115           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8116   }
8117   else if (element == EL_BD_AMOEBA)
8118   {
8119     num_contents = 1;
8120     content_xsize = 1;
8121     content_ysize = 1;
8122
8123     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8124       for (y = 0; y < 3; y++)
8125         for (x = 0; x < 3; x++)
8126           content_array[i][x][y] = EL_EMPTY;
8127     content_array[0][0][0] = level->amoeba_content;
8128   }
8129   else
8130   {
8131     // chunk header already written -- write empty chunk data
8132     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8133
8134     Warn("cannot save content for element '%d'", element);
8135
8136     return;
8137   }
8138
8139   putFile16BitBE(file, element);
8140   putFile8Bit(file, num_contents);
8141   putFile8Bit(file, content_xsize);
8142   putFile8Bit(file, content_ysize);
8143
8144   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8145
8146   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8147     for (y = 0; y < 3; y++)
8148       for (x = 0; x < 3; x++)
8149         putFile16BitBE(file, content_array[i][x][y]);
8150 }
8151 #endif
8152
8153 #if ENABLE_HISTORIC_CHUNKS
8154 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8155 {
8156   int envelope_nr = element - EL_ENVELOPE_1;
8157   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8158   int chunk_size = 0;
8159   int i;
8160
8161   chunk_size += putFile16BitBE(file, element);
8162   chunk_size += putFile16BitBE(file, envelope_len);
8163   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8164   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8165
8166   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8167   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8168
8169   for (i = 0; i < envelope_len; i++)
8170     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8171
8172   return chunk_size;
8173 }
8174 #endif
8175
8176 #if ENABLE_HISTORIC_CHUNKS
8177 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8178                            int num_changed_custom_elements)
8179 {
8180   int i, check = 0;
8181
8182   putFile16BitBE(file, num_changed_custom_elements);
8183
8184   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8185   {
8186     int element = EL_CUSTOM_START + i;
8187
8188     struct ElementInfo *ei = &element_info[element];
8189
8190     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8191     {
8192       if (check < num_changed_custom_elements)
8193       {
8194         putFile16BitBE(file, element);
8195         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8196       }
8197
8198       check++;
8199     }
8200   }
8201
8202   if (check != num_changed_custom_elements)     // should not happen
8203     Warn("inconsistent number of custom element properties");
8204 }
8205 #endif
8206
8207 #if ENABLE_HISTORIC_CHUNKS
8208 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8209                            int num_changed_custom_elements)
8210 {
8211   int i, check = 0;
8212
8213   putFile16BitBE(file, num_changed_custom_elements);
8214
8215   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8216   {
8217     int element = EL_CUSTOM_START + i;
8218
8219     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8220     {
8221       if (check < num_changed_custom_elements)
8222       {
8223         putFile16BitBE(file, element);
8224         putFile16BitBE(file, element_info[element].change->target_element);
8225       }
8226
8227       check++;
8228     }
8229   }
8230
8231   if (check != num_changed_custom_elements)     // should not happen
8232     Warn("inconsistent number of custom target elements");
8233 }
8234 #endif
8235
8236 #if ENABLE_HISTORIC_CHUNKS
8237 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8238                            int num_changed_custom_elements)
8239 {
8240   int i, j, x, y, check = 0;
8241
8242   putFile16BitBE(file, num_changed_custom_elements);
8243
8244   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8245   {
8246     int element = EL_CUSTOM_START + i;
8247     struct ElementInfo *ei = &element_info[element];
8248
8249     if (ei->modified_settings)
8250     {
8251       if (check < num_changed_custom_elements)
8252       {
8253         putFile16BitBE(file, element);
8254
8255         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8256           putFile8Bit(file, ei->description[j]);
8257
8258         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8259
8260         // some free bytes for future properties and padding
8261         WriteUnusedBytesToFile(file, 7);
8262
8263         putFile8Bit(file, ei->use_gfx_element);
8264         putFile16BitBE(file, ei->gfx_element_initial);
8265
8266         putFile8Bit(file, ei->collect_score_initial);
8267         putFile8Bit(file, ei->collect_count_initial);
8268
8269         putFile16BitBE(file, ei->push_delay_fixed);
8270         putFile16BitBE(file, ei->push_delay_random);
8271         putFile16BitBE(file, ei->move_delay_fixed);
8272         putFile16BitBE(file, ei->move_delay_random);
8273
8274         putFile16BitBE(file, ei->move_pattern);
8275         putFile8Bit(file, ei->move_direction_initial);
8276         putFile8Bit(file, ei->move_stepsize);
8277
8278         for (y = 0; y < 3; y++)
8279           for (x = 0; x < 3; x++)
8280             putFile16BitBE(file, ei->content.e[x][y]);
8281
8282         putFile32BitBE(file, ei->change->events);
8283
8284         putFile16BitBE(file, ei->change->target_element);
8285
8286         putFile16BitBE(file, ei->change->delay_fixed);
8287         putFile16BitBE(file, ei->change->delay_random);
8288         putFile16BitBE(file, ei->change->delay_frames);
8289
8290         putFile16BitBE(file, ei->change->initial_trigger_element);
8291
8292         putFile8Bit(file, ei->change->explode);
8293         putFile8Bit(file, ei->change->use_target_content);
8294         putFile8Bit(file, ei->change->only_if_complete);
8295         putFile8Bit(file, ei->change->use_random_replace);
8296
8297         putFile8Bit(file, ei->change->random_percentage);
8298         putFile8Bit(file, ei->change->replace_when);
8299
8300         for (y = 0; y < 3; y++)
8301           for (x = 0; x < 3; x++)
8302             putFile16BitBE(file, ei->change->content.e[x][y]);
8303
8304         putFile8Bit(file, ei->slippery_type);
8305
8306         // some free bytes for future properties and padding
8307         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
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_CUS4(FILE *file, struct LevelInfo *level, int element)
8321 {
8322   struct ElementInfo *ei = &element_info[element];
8323   int i, j, x, y;
8324
8325   // ---------- custom element base property values (96 bytes) ----------------
8326
8327   putFile16BitBE(file, element);
8328
8329   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8330     putFile8Bit(file, ei->description[i]);
8331
8332   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8333
8334   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8335
8336   putFile8Bit(file, ei->num_change_pages);
8337
8338   putFile16BitBE(file, ei->ce_value_fixed_initial);
8339   putFile16BitBE(file, ei->ce_value_random_initial);
8340   putFile8Bit(file, ei->use_last_ce_value);
8341
8342   putFile8Bit(file, ei->use_gfx_element);
8343   putFile16BitBE(file, ei->gfx_element_initial);
8344
8345   putFile8Bit(file, ei->collect_score_initial);
8346   putFile8Bit(file, ei->collect_count_initial);
8347
8348   putFile8Bit(file, ei->drop_delay_fixed);
8349   putFile8Bit(file, ei->push_delay_fixed);
8350   putFile8Bit(file, ei->drop_delay_random);
8351   putFile8Bit(file, ei->push_delay_random);
8352   putFile16BitBE(file, ei->move_delay_fixed);
8353   putFile16BitBE(file, ei->move_delay_random);
8354
8355   // bits 0 - 15 of "move_pattern" ...
8356   putFile16BitBE(file, ei->move_pattern & 0xffff);
8357   putFile8Bit(file, ei->move_direction_initial);
8358   putFile8Bit(file, ei->move_stepsize);
8359
8360   putFile8Bit(file, ei->slippery_type);
8361
8362   for (y = 0; y < 3; y++)
8363     for (x = 0; x < 3; x++)
8364       putFile16BitBE(file, ei->content.e[x][y]);
8365
8366   putFile16BitBE(file, ei->move_enter_element);
8367   putFile16BitBE(file, ei->move_leave_element);
8368   putFile8Bit(file, ei->move_leave_type);
8369
8370   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8371   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8372
8373   putFile8Bit(file, ei->access_direction);
8374
8375   putFile8Bit(file, ei->explosion_delay);
8376   putFile8Bit(file, ei->ignition_delay);
8377   putFile8Bit(file, ei->explosion_type);
8378
8379   // some free bytes for future custom property values and padding
8380   WriteUnusedBytesToFile(file, 1);
8381
8382   // ---------- change page property values (48 bytes) ------------------------
8383
8384   for (i = 0; i < ei->num_change_pages; i++)
8385   {
8386     struct ElementChangeInfo *change = &ei->change_page[i];
8387     unsigned int event_bits;
8388
8389     // bits 0 - 31 of "has_event[]" ...
8390     event_bits = 0;
8391     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8392       if (change->has_event[j])
8393         event_bits |= (1u << j);
8394     putFile32BitBE(file, event_bits);
8395
8396     putFile16BitBE(file, change->target_element);
8397
8398     putFile16BitBE(file, change->delay_fixed);
8399     putFile16BitBE(file, change->delay_random);
8400     putFile16BitBE(file, change->delay_frames);
8401
8402     putFile16BitBE(file, change->initial_trigger_element);
8403
8404     putFile8Bit(file, change->explode);
8405     putFile8Bit(file, change->use_target_content);
8406     putFile8Bit(file, change->only_if_complete);
8407     putFile8Bit(file, change->use_random_replace);
8408
8409     putFile8Bit(file, change->random_percentage);
8410     putFile8Bit(file, change->replace_when);
8411
8412     for (y = 0; y < 3; y++)
8413       for (x = 0; x < 3; x++)
8414         putFile16BitBE(file, change->target_content.e[x][y]);
8415
8416     putFile8Bit(file, change->can_change);
8417
8418     putFile8Bit(file, change->trigger_side);
8419
8420     putFile8Bit(file, change->trigger_player);
8421     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8422                        log_2(change->trigger_page)));
8423
8424     putFile8Bit(file, change->has_action);
8425     putFile8Bit(file, change->action_type);
8426     putFile8Bit(file, change->action_mode);
8427     putFile16BitBE(file, change->action_arg);
8428
8429     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8430     event_bits = 0;
8431     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8432       if (change->has_event[j])
8433         event_bits |= (1u << (j - 32));
8434     putFile8Bit(file, event_bits);
8435   }
8436 }
8437 #endif
8438
8439 #if ENABLE_HISTORIC_CHUNKS
8440 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8441 {
8442   struct ElementInfo *ei = &element_info[element];
8443   struct ElementGroupInfo *group = ei->group;
8444   int i;
8445
8446   putFile16BitBE(file, element);
8447
8448   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8449     putFile8Bit(file, ei->description[i]);
8450
8451   putFile8Bit(file, group->num_elements);
8452
8453   putFile8Bit(file, ei->use_gfx_element);
8454   putFile16BitBE(file, ei->gfx_element_initial);
8455
8456   putFile8Bit(file, group->choice_mode);
8457
8458   // some free bytes for future values and padding
8459   WriteUnusedBytesToFile(file, 3);
8460
8461   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8462     putFile16BitBE(file, group->element[i]);
8463 }
8464 #endif
8465
8466 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8467                                 boolean write_element)
8468 {
8469   int save_type = entry->save_type;
8470   int data_type = entry->data_type;
8471   int conf_type = entry->conf_type;
8472   int byte_mask = conf_type & CONF_MASK_BYTES;
8473   int element = entry->element;
8474   int default_value = entry->default_value;
8475   int num_bytes = 0;
8476   boolean modified = FALSE;
8477
8478   if (byte_mask != CONF_MASK_MULTI_BYTES)
8479   {
8480     void *value_ptr = entry->value;
8481     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8482                  *(int *)value_ptr);
8483
8484     // check if any settings have been modified before saving them
8485     if (value != default_value)
8486       modified = TRUE;
8487
8488     // do not save if explicitly told or if unmodified default settings
8489     if ((save_type == SAVE_CONF_NEVER) ||
8490         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8491       return 0;
8492
8493     if (write_element)
8494       num_bytes += putFile16BitBE(file, element);
8495
8496     num_bytes += putFile8Bit(file, conf_type);
8497     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8498                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8499                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8500                   0);
8501   }
8502   else if (data_type == TYPE_STRING)
8503   {
8504     char *default_string = entry->default_string;
8505     char *string = (char *)(entry->value);
8506     int string_length = strlen(string);
8507     int i;
8508
8509     // check if any settings have been modified before saving them
8510     if (!strEqual(string, default_string))
8511       modified = TRUE;
8512
8513     // do not save if explicitly told or if unmodified default settings
8514     if ((save_type == SAVE_CONF_NEVER) ||
8515         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8516       return 0;
8517
8518     if (write_element)
8519       num_bytes += putFile16BitBE(file, element);
8520
8521     num_bytes += putFile8Bit(file, conf_type);
8522     num_bytes += putFile16BitBE(file, string_length);
8523
8524     for (i = 0; i < string_length; i++)
8525       num_bytes += putFile8Bit(file, string[i]);
8526   }
8527   else if (data_type == TYPE_ELEMENT_LIST)
8528   {
8529     int *element_array = (int *)(entry->value);
8530     int num_elements = *(int *)(entry->num_entities);
8531     int i;
8532
8533     // check if any settings have been modified before saving them
8534     for (i = 0; i < num_elements; i++)
8535       if (element_array[i] != default_value)
8536         modified = TRUE;
8537
8538     // do not save if explicitly told or if unmodified default settings
8539     if ((save_type == SAVE_CONF_NEVER) ||
8540         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8541       return 0;
8542
8543     if (write_element)
8544       num_bytes += putFile16BitBE(file, element);
8545
8546     num_bytes += putFile8Bit(file, conf_type);
8547     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8548
8549     for (i = 0; i < num_elements; i++)
8550       num_bytes += putFile16BitBE(file, element_array[i]);
8551   }
8552   else if (data_type == TYPE_CONTENT_LIST)
8553   {
8554     struct Content *content = (struct Content *)(entry->value);
8555     int num_contents = *(int *)(entry->num_entities);
8556     int i, x, y;
8557
8558     // check if any settings have been modified before saving them
8559     for (i = 0; i < num_contents; i++)
8560       for (y = 0; y < 3; y++)
8561         for (x = 0; x < 3; x++)
8562           if (content[i].e[x][y] != default_value)
8563             modified = TRUE;
8564
8565     // do not save if explicitly told or if unmodified default settings
8566     if ((save_type == SAVE_CONF_NEVER) ||
8567         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8568       return 0;
8569
8570     if (write_element)
8571       num_bytes += putFile16BitBE(file, element);
8572
8573     num_bytes += putFile8Bit(file, conf_type);
8574     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8575
8576     for (i = 0; i < num_contents; i++)
8577       for (y = 0; y < 3; y++)
8578         for (x = 0; x < 3; x++)
8579           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8580   }
8581
8582   return num_bytes;
8583 }
8584
8585 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8586 {
8587   int chunk_size = 0;
8588   int i;
8589
8590   li = *level;          // copy level data into temporary buffer
8591
8592   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8593     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8594
8595   return chunk_size;
8596 }
8597
8598 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8599 {
8600   int chunk_size = 0;
8601   int i;
8602
8603   li = *level;          // copy level data into temporary buffer
8604
8605   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8606     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8607
8608   return chunk_size;
8609 }
8610
8611 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8612 {
8613   int envelope_nr = element - EL_ENVELOPE_1;
8614   int chunk_size = 0;
8615   int i;
8616
8617   chunk_size += putFile16BitBE(file, element);
8618
8619   // copy envelope data into temporary buffer
8620   xx_envelope = level->envelope[envelope_nr];
8621
8622   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8623     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8624
8625   return chunk_size;
8626 }
8627
8628 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8629 {
8630   struct ElementInfo *ei = &element_info[element];
8631   int chunk_size = 0;
8632   int i, j;
8633
8634   chunk_size += putFile16BitBE(file, element);
8635
8636   xx_ei = *ei;          // copy element data into temporary buffer
8637
8638   // set default description string for this specific element
8639   strcpy(xx_default_description, getDefaultElementDescription(ei));
8640
8641   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8642     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8643
8644   for (i = 0; i < ei->num_change_pages; i++)
8645   {
8646     struct ElementChangeInfo *change = &ei->change_page[i];
8647
8648     xx_current_change_page = i;
8649
8650     xx_change = *change;        // copy change data into temporary buffer
8651
8652     resetEventBits();
8653     setEventBitsFromEventFlags(change);
8654
8655     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8656       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8657                                          FALSE);
8658   }
8659
8660   return chunk_size;
8661 }
8662
8663 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8664 {
8665   struct ElementInfo *ei = &element_info[element];
8666   struct ElementGroupInfo *group = ei->group;
8667   int chunk_size = 0;
8668   int i;
8669
8670   chunk_size += putFile16BitBE(file, element);
8671
8672   xx_ei = *ei;          // copy element data into temporary buffer
8673   xx_group = *group;    // copy group data into temporary buffer
8674
8675   // set default description string for this specific element
8676   strcpy(xx_default_description, getDefaultElementDescription(ei));
8677
8678   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8679     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8680
8681   return chunk_size;
8682 }
8683
8684 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8685 {
8686   struct ElementInfo *ei = &element_info[element];
8687   int chunk_size = 0;
8688   int i;
8689
8690   chunk_size += putFile16BitBE(file, element);
8691
8692   xx_ei = *ei;          // copy element data into temporary buffer
8693
8694   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8695     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8696
8697   return chunk_size;
8698 }
8699
8700 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8701                                   boolean save_as_template)
8702 {
8703   int chunk_size;
8704   int i;
8705   FILE *file;
8706
8707   if (!(file = fopen(filename, MODE_WRITE)))
8708   {
8709     Warn("cannot save level file '%s'", filename);
8710
8711     return;
8712   }
8713
8714   level->file_version = FILE_VERSION_ACTUAL;
8715   level->game_version = GAME_VERSION_ACTUAL;
8716
8717   level->creation_date = getCurrentDate();
8718
8719   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8720   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8721
8722   chunk_size = SaveLevel_VERS(NULL, level);
8723   putFileChunkBE(file, "VERS", chunk_size);
8724   SaveLevel_VERS(file, level);
8725
8726   chunk_size = SaveLevel_DATE(NULL, level);
8727   putFileChunkBE(file, "DATE", chunk_size);
8728   SaveLevel_DATE(file, level);
8729
8730   chunk_size = SaveLevel_NAME(NULL, level);
8731   putFileChunkBE(file, "NAME", chunk_size);
8732   SaveLevel_NAME(file, level);
8733
8734   chunk_size = SaveLevel_AUTH(NULL, level);
8735   putFileChunkBE(file, "AUTH", chunk_size);
8736   SaveLevel_AUTH(file, level);
8737
8738   chunk_size = SaveLevel_INFO(NULL, level);
8739   putFileChunkBE(file, "INFO", chunk_size);
8740   SaveLevel_INFO(file, level);
8741
8742   chunk_size = SaveLevel_BODY(NULL, level);
8743   putFileChunkBE(file, "BODY", chunk_size);
8744   SaveLevel_BODY(file, level);
8745
8746   chunk_size = SaveLevel_ELEM(NULL, level);
8747   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8748   {
8749     putFileChunkBE(file, "ELEM", chunk_size);
8750     SaveLevel_ELEM(file, level);
8751   }
8752
8753   for (i = 0; i < NUM_ENVELOPES; i++)
8754   {
8755     int element = EL_ENVELOPE_1 + i;
8756
8757     chunk_size = SaveLevel_NOTE(NULL, level, element);
8758     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8759     {
8760       putFileChunkBE(file, "NOTE", chunk_size);
8761       SaveLevel_NOTE(file, level, element);
8762     }
8763   }
8764
8765   // if not using template level, check for non-default custom/group elements
8766   if (!level->use_custom_template || save_as_template)
8767   {
8768     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8769     {
8770       int element = EL_CUSTOM_START + i;
8771
8772       chunk_size = SaveLevel_CUSX(NULL, level, element);
8773       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8774       {
8775         putFileChunkBE(file, "CUSX", chunk_size);
8776         SaveLevel_CUSX(file, level, element);
8777       }
8778     }
8779
8780     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8781     {
8782       int element = EL_GROUP_START + i;
8783
8784       chunk_size = SaveLevel_GRPX(NULL, level, element);
8785       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8786       {
8787         putFileChunkBE(file, "GRPX", chunk_size);
8788         SaveLevel_GRPX(file, level, element);
8789       }
8790     }
8791
8792     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8793     {
8794       int element = GET_EMPTY_ELEMENT(i);
8795
8796       chunk_size = SaveLevel_EMPX(NULL, level, element);
8797       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8798       {
8799         putFileChunkBE(file, "EMPX", chunk_size);
8800         SaveLevel_EMPX(file, level, element);
8801       }
8802     }
8803   }
8804
8805   fclose(file);
8806
8807   SetFilePermissions(filename, PERMS_PRIVATE);
8808 }
8809
8810 void SaveLevel(int nr)
8811 {
8812   char *filename = getDefaultLevelFilename(nr);
8813
8814   SaveLevelFromFilename(&level, filename, FALSE);
8815 }
8816
8817 void SaveLevelTemplate(void)
8818 {
8819   char *filename = getLocalLevelTemplateFilename();
8820
8821   SaveLevelFromFilename(&level, filename, TRUE);
8822 }
8823
8824 boolean SaveLevelChecked(int nr)
8825 {
8826   char *filename = getDefaultLevelFilename(nr);
8827   boolean new_level = !fileExists(filename);
8828   boolean level_saved = FALSE;
8829
8830   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8831   {
8832     SaveLevel(nr);
8833
8834     if (new_level)
8835       Request("Level saved!", REQ_CONFIRM);
8836
8837     level_saved = TRUE;
8838   }
8839
8840   return level_saved;
8841 }
8842
8843 void DumpLevel(struct LevelInfo *level)
8844 {
8845   if (level->no_level_file || level->no_valid_file)
8846   {
8847     Warn("cannot dump -- no valid level file found");
8848
8849     return;
8850   }
8851
8852   PrintLine("-", 79);
8853   Print("Level xxx (file version %08d, game version %08d)\n",
8854         level->file_version, level->game_version);
8855   PrintLine("-", 79);
8856
8857   Print("Level author: '%s'\n", level->author);
8858   Print("Level title:  '%s'\n", level->name);
8859   Print("\n");
8860   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8861   Print("\n");
8862   Print("Level time:  %d seconds\n", level->time);
8863   Print("Gems needed: %d\n", level->gems_needed);
8864   Print("\n");
8865   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8866   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8867   Print("Time for light:      %d seconds\n", level->time_light);
8868   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8869   Print("\n");
8870   Print("Amoeba speed: %d\n", level->amoeba_speed);
8871   Print("\n");
8872
8873   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8874   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8875   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8876   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8877   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8878   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8879
8880   if (options.debug)
8881   {
8882     int i, j;
8883
8884     for (i = 0; i < NUM_ENVELOPES; i++)
8885     {
8886       char *text = level->envelope[i].text;
8887       int text_len = strlen(text);
8888       boolean has_text = FALSE;
8889
8890       for (j = 0; j < text_len; j++)
8891         if (text[j] != ' ' && text[j] != '\n')
8892           has_text = TRUE;
8893
8894       if (has_text)
8895       {
8896         Print("\n");
8897         Print("Envelope %d:\n'%s'\n", i + 1, text);
8898       }
8899     }
8900   }
8901
8902   PrintLine("-", 79);
8903 }
8904
8905 void DumpLevels(void)
8906 {
8907   static LevelDirTree *dumplevel_leveldir = NULL;
8908
8909   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8910                                                  global.dumplevel_leveldir);
8911
8912   if (dumplevel_leveldir == NULL)
8913     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8914
8915   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8916       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8917     Fail("no such level number: %d", global.dumplevel_level_nr);
8918
8919   leveldir_current = dumplevel_leveldir;
8920
8921   LoadLevel(global.dumplevel_level_nr);
8922   DumpLevel(&level);
8923
8924   CloseAllAndExit(0);
8925 }
8926
8927
8928 // ============================================================================
8929 // tape file functions
8930 // ============================================================================
8931
8932 static void setTapeInfoToDefaults(void)
8933 {
8934   int i;
8935
8936   // always start with reliable default values (empty tape)
8937   TapeErase();
8938
8939   // default values (also for pre-1.2 tapes) with only the first player
8940   tape.player_participates[0] = TRUE;
8941   for (i = 1; i < MAX_PLAYERS; i++)
8942     tape.player_participates[i] = FALSE;
8943
8944   // at least one (default: the first) player participates in every tape
8945   tape.num_participating_players = 1;
8946
8947   tape.property_bits = TAPE_PROPERTY_NONE;
8948
8949   tape.level_nr = level_nr;
8950   tape.counter = 0;
8951   tape.changed = FALSE;
8952   tape.solved = FALSE;
8953
8954   tape.recording = FALSE;
8955   tape.playing = FALSE;
8956   tape.pausing = FALSE;
8957
8958   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8959   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8960
8961   tape.no_info_chunk = TRUE;
8962   tape.no_valid_file = FALSE;
8963 }
8964
8965 static int getTapePosSize(struct TapeInfo *tape)
8966 {
8967   int tape_pos_size = 0;
8968
8969   if (tape->use_key_actions)
8970     tape_pos_size += tape->num_participating_players;
8971
8972   if (tape->use_mouse_actions)
8973     tape_pos_size += 3;         // x and y position and mouse button mask
8974
8975   tape_pos_size += 1;           // tape action delay value
8976
8977   return tape_pos_size;
8978 }
8979
8980 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8981 {
8982   tape->use_key_actions = FALSE;
8983   tape->use_mouse_actions = FALSE;
8984
8985   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8986     tape->use_key_actions = TRUE;
8987
8988   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8989     tape->use_mouse_actions = TRUE;
8990 }
8991
8992 static int getTapeActionValue(struct TapeInfo *tape)
8993 {
8994   return (tape->use_key_actions &&
8995           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8996           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8997           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8998           TAPE_ACTIONS_DEFAULT);
8999 }
9000
9001 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9002 {
9003   tape->file_version = getFileVersion(file);
9004   tape->game_version = getFileVersion(file);
9005
9006   return chunk_size;
9007 }
9008
9009 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9010 {
9011   int i;
9012
9013   tape->random_seed = getFile32BitBE(file);
9014   tape->date        = getFile32BitBE(file);
9015   tape->length      = getFile32BitBE(file);
9016
9017   // read header fields that are new since version 1.2
9018   if (tape->file_version >= FILE_VERSION_1_2)
9019   {
9020     byte store_participating_players = getFile8Bit(file);
9021     int engine_version;
9022
9023     // since version 1.2, tapes store which players participate in the tape
9024     tape->num_participating_players = 0;
9025     for (i = 0; i < MAX_PLAYERS; i++)
9026     {
9027       tape->player_participates[i] = FALSE;
9028
9029       if (store_participating_players & (1 << i))
9030       {
9031         tape->player_participates[i] = TRUE;
9032         tape->num_participating_players++;
9033       }
9034     }
9035
9036     setTapeActionFlags(tape, getFile8Bit(file));
9037
9038     tape->property_bits = getFile8Bit(file);
9039     tape->solved = getFile8Bit(file);
9040
9041     engine_version = getFileVersion(file);
9042     if (engine_version > 0)
9043       tape->engine_version = engine_version;
9044     else
9045       tape->engine_version = tape->game_version;
9046   }
9047
9048   return chunk_size;
9049 }
9050
9051 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9052 {
9053   tape->scr_fieldx = getFile8Bit(file);
9054   tape->scr_fieldy = getFile8Bit(file);
9055
9056   return chunk_size;
9057 }
9058
9059 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9060 {
9061   char *level_identifier = NULL;
9062   int level_identifier_size;
9063   int i;
9064
9065   tape->no_info_chunk = FALSE;
9066
9067   level_identifier_size = getFile16BitBE(file);
9068
9069   level_identifier = checked_malloc(level_identifier_size);
9070
9071   for (i = 0; i < level_identifier_size; i++)
9072     level_identifier[i] = getFile8Bit(file);
9073
9074   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9075   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9076
9077   checked_free(level_identifier);
9078
9079   tape->level_nr = getFile16BitBE(file);
9080
9081   chunk_size = 2 + level_identifier_size + 2;
9082
9083   return chunk_size;
9084 }
9085
9086 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9087 {
9088   int i, j;
9089   int tape_pos_size = getTapePosSize(tape);
9090   int chunk_size_expected = tape_pos_size * tape->length;
9091
9092   if (chunk_size_expected != chunk_size)
9093   {
9094     ReadUnusedBytesFromFile(file, chunk_size);
9095     return chunk_size_expected;
9096   }
9097
9098   for (i = 0; i < tape->length; i++)
9099   {
9100     if (i >= MAX_TAPE_LEN)
9101     {
9102       Warn("tape truncated -- size exceeds maximum tape size %d",
9103             MAX_TAPE_LEN);
9104
9105       // tape too large; read and ignore remaining tape data from this chunk
9106       for (;i < tape->length; i++)
9107         ReadUnusedBytesFromFile(file, tape_pos_size);
9108
9109       break;
9110     }
9111
9112     if (tape->use_key_actions)
9113     {
9114       for (j = 0; j < MAX_PLAYERS; j++)
9115       {
9116         tape->pos[i].action[j] = MV_NONE;
9117
9118         if (tape->player_participates[j])
9119           tape->pos[i].action[j] = getFile8Bit(file);
9120       }
9121     }
9122
9123     if (tape->use_mouse_actions)
9124     {
9125       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9126       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9127       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9128     }
9129
9130     tape->pos[i].delay = getFile8Bit(file);
9131
9132     if (tape->file_version == FILE_VERSION_1_0)
9133     {
9134       // eliminate possible diagonal moves in old tapes
9135       // this is only for backward compatibility
9136
9137       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9138       byte action = tape->pos[i].action[0];
9139       int k, num_moves = 0;
9140
9141       for (k = 0; k < 4; k++)
9142       {
9143         if (action & joy_dir[k])
9144         {
9145           tape->pos[i + num_moves].action[0] = joy_dir[k];
9146           if (num_moves > 0)
9147             tape->pos[i + num_moves].delay = 0;
9148           num_moves++;
9149         }
9150       }
9151
9152       if (num_moves > 1)
9153       {
9154         num_moves--;
9155         i += num_moves;
9156         tape->length += num_moves;
9157       }
9158     }
9159     else if (tape->file_version < FILE_VERSION_2_0)
9160     {
9161       // convert pre-2.0 tapes to new tape format
9162
9163       if (tape->pos[i].delay > 1)
9164       {
9165         // action part
9166         tape->pos[i + 1] = tape->pos[i];
9167         tape->pos[i + 1].delay = 1;
9168
9169         // delay part
9170         for (j = 0; j < MAX_PLAYERS; j++)
9171           tape->pos[i].action[j] = MV_NONE;
9172         tape->pos[i].delay--;
9173
9174         i++;
9175         tape->length++;
9176       }
9177     }
9178
9179     if (checkEndOfFile(file))
9180       break;
9181   }
9182
9183   if (i != tape->length)
9184     chunk_size = tape_pos_size * i;
9185
9186   return chunk_size;
9187 }
9188
9189 static void LoadTape_SokobanSolution(char *filename)
9190 {
9191   File *file;
9192   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9193
9194   if (!(file = openFile(filename, MODE_READ)))
9195   {
9196     tape.no_valid_file = TRUE;
9197
9198     return;
9199   }
9200
9201   while (!checkEndOfFile(file))
9202   {
9203     unsigned char c = getByteFromFile(file);
9204
9205     if (checkEndOfFile(file))
9206       break;
9207
9208     switch (c)
9209     {
9210       case 'u':
9211       case 'U':
9212         tape.pos[tape.length].action[0] = MV_UP;
9213         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9214         tape.length++;
9215         break;
9216
9217       case 'd':
9218       case 'D':
9219         tape.pos[tape.length].action[0] = MV_DOWN;
9220         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9221         tape.length++;
9222         break;
9223
9224       case 'l':
9225       case 'L':
9226         tape.pos[tape.length].action[0] = MV_LEFT;
9227         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9228         tape.length++;
9229         break;
9230
9231       case 'r':
9232       case 'R':
9233         tape.pos[tape.length].action[0] = MV_RIGHT;
9234         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9235         tape.length++;
9236         break;
9237
9238       case '\n':
9239       case '\r':
9240       case '\t':
9241       case ' ':
9242         // ignore white-space characters
9243         break;
9244
9245       default:
9246         tape.no_valid_file = TRUE;
9247
9248         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9249
9250         break;
9251     }
9252   }
9253
9254   closeFile(file);
9255
9256   if (tape.no_valid_file)
9257     return;
9258
9259   tape.length_frames  = GetTapeLengthFrames();
9260   tape.length_seconds = GetTapeLengthSeconds();
9261 }
9262
9263 void LoadTapeFromFilename(char *filename)
9264 {
9265   char cookie[MAX_LINE_LEN];
9266   char chunk_name[CHUNK_ID_LEN + 1];
9267   File *file;
9268   int chunk_size;
9269
9270   // always start with reliable default values
9271   setTapeInfoToDefaults();
9272
9273   if (strSuffix(filename, ".sln"))
9274   {
9275     LoadTape_SokobanSolution(filename);
9276
9277     return;
9278   }
9279
9280   if (!(file = openFile(filename, MODE_READ)))
9281   {
9282     tape.no_valid_file = TRUE;
9283
9284     return;
9285   }
9286
9287   getFileChunkBE(file, chunk_name, NULL);
9288   if (strEqual(chunk_name, "RND1"))
9289   {
9290     getFile32BitBE(file);               // not used
9291
9292     getFileChunkBE(file, chunk_name, NULL);
9293     if (!strEqual(chunk_name, "TAPE"))
9294     {
9295       tape.no_valid_file = TRUE;
9296
9297       Warn("unknown format of tape file '%s'", filename);
9298
9299       closeFile(file);
9300
9301       return;
9302     }
9303   }
9304   else  // check for pre-2.0 file format with cookie string
9305   {
9306     strcpy(cookie, chunk_name);
9307     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9308       cookie[4] = '\0';
9309     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9310       cookie[strlen(cookie) - 1] = '\0';
9311
9312     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9313     {
9314       tape.no_valid_file = TRUE;
9315
9316       Warn("unknown format of tape file '%s'", filename);
9317
9318       closeFile(file);
9319
9320       return;
9321     }
9322
9323     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9324     {
9325       tape.no_valid_file = TRUE;
9326
9327       Warn("unsupported version of tape file '%s'", filename);
9328
9329       closeFile(file);
9330
9331       return;
9332     }
9333
9334     // pre-2.0 tape files have no game version, so use file version here
9335     tape.game_version = tape.file_version;
9336   }
9337
9338   if (tape.file_version < FILE_VERSION_1_2)
9339   {
9340     // tape files from versions before 1.2.0 without chunk structure
9341     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9342     LoadTape_BODY(file, 2 * tape.length,      &tape);
9343   }
9344   else
9345   {
9346     static struct
9347     {
9348       char *name;
9349       int size;
9350       int (*loader)(File *, int, struct TapeInfo *);
9351     }
9352     chunk_info[] =
9353     {
9354       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9355       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9356       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9357       { "INFO", -1,                     LoadTape_INFO },
9358       { "BODY", -1,                     LoadTape_BODY },
9359       {  NULL,  0,                      NULL }
9360     };
9361
9362     while (getFileChunkBE(file, chunk_name, &chunk_size))
9363     {
9364       int i = 0;
9365
9366       while (chunk_info[i].name != NULL &&
9367              !strEqual(chunk_name, chunk_info[i].name))
9368         i++;
9369
9370       if (chunk_info[i].name == NULL)
9371       {
9372         Warn("unknown chunk '%s' in tape file '%s'",
9373               chunk_name, filename);
9374
9375         ReadUnusedBytesFromFile(file, chunk_size);
9376       }
9377       else if (chunk_info[i].size != -1 &&
9378                chunk_info[i].size != chunk_size)
9379       {
9380         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9381               chunk_size, chunk_name, filename);
9382
9383         ReadUnusedBytesFromFile(file, chunk_size);
9384       }
9385       else
9386       {
9387         // call function to load this tape chunk
9388         int chunk_size_expected =
9389           (chunk_info[i].loader)(file, chunk_size, &tape);
9390
9391         // the size of some chunks cannot be checked before reading other
9392         // chunks first (like "HEAD" and "BODY") that contain some header
9393         // information, so check them here
9394         if (chunk_size_expected != chunk_size)
9395         {
9396           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9397                 chunk_size, chunk_name, filename);
9398         }
9399       }
9400     }
9401   }
9402
9403   closeFile(file);
9404
9405   tape.length_frames  = GetTapeLengthFrames();
9406   tape.length_seconds = GetTapeLengthSeconds();
9407
9408 #if 0
9409   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9410         tape.file_version);
9411   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9412         tape.game_version);
9413   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9414         tape.engine_version);
9415 #endif
9416 }
9417
9418 void LoadTape(int nr)
9419 {
9420   char *filename = getTapeFilename(nr);
9421
9422   LoadTapeFromFilename(filename);
9423 }
9424
9425 void LoadSolutionTape(int nr)
9426 {
9427   char *filename = getSolutionTapeFilename(nr);
9428
9429   LoadTapeFromFilename(filename);
9430
9431   if (TAPE_IS_EMPTY(tape))
9432   {
9433     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9434         level.native_bd_level->replay != NULL)
9435       CopyNativeTape_BD_to_RND(&level);
9436     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9437         level.native_sp_level->demo.is_available)
9438       CopyNativeTape_SP_to_RND(&level);
9439   }
9440 }
9441
9442 void LoadScoreTape(char *score_tape_basename, int nr)
9443 {
9444   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9445
9446   LoadTapeFromFilename(filename);
9447 }
9448
9449 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9450 {
9451   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9452
9453   LoadTapeFromFilename(filename);
9454 }
9455
9456 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9457 {
9458   // chunk required for team mode tapes with non-default screen size
9459   return (tape->num_participating_players > 1 &&
9460           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9461            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9462 }
9463
9464 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9465 {
9466   putFileVersion(file, tape->file_version);
9467   putFileVersion(file, tape->game_version);
9468 }
9469
9470 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9471 {
9472   int i;
9473   byte store_participating_players = 0;
9474
9475   // set bits for participating players for compact storage
9476   for (i = 0; i < MAX_PLAYERS; i++)
9477     if (tape->player_participates[i])
9478       store_participating_players |= (1 << i);
9479
9480   putFile32BitBE(file, tape->random_seed);
9481   putFile32BitBE(file, tape->date);
9482   putFile32BitBE(file, tape->length);
9483
9484   putFile8Bit(file, store_participating_players);
9485
9486   putFile8Bit(file, getTapeActionValue(tape));
9487
9488   putFile8Bit(file, tape->property_bits);
9489   putFile8Bit(file, tape->solved);
9490
9491   putFileVersion(file, tape->engine_version);
9492 }
9493
9494 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9495 {
9496   putFile8Bit(file, tape->scr_fieldx);
9497   putFile8Bit(file, tape->scr_fieldy);
9498 }
9499
9500 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9501 {
9502   int level_identifier_size = strlen(tape->level_identifier) + 1;
9503   int i;
9504
9505   putFile16BitBE(file, level_identifier_size);
9506
9507   for (i = 0; i < level_identifier_size; i++)
9508     putFile8Bit(file, tape->level_identifier[i]);
9509
9510   putFile16BitBE(file, tape->level_nr);
9511 }
9512
9513 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9514 {
9515   int i, j;
9516
9517   for (i = 0; i < tape->length; i++)
9518   {
9519     if (tape->use_key_actions)
9520     {
9521       for (j = 0; j < MAX_PLAYERS; j++)
9522         if (tape->player_participates[j])
9523           putFile8Bit(file, tape->pos[i].action[j]);
9524     }
9525
9526     if (tape->use_mouse_actions)
9527     {
9528       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9529       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9530       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9531     }
9532
9533     putFile8Bit(file, tape->pos[i].delay);
9534   }
9535 }
9536
9537 void SaveTapeToFilename(char *filename)
9538 {
9539   FILE *file;
9540   int tape_pos_size;
9541   int info_chunk_size;
9542   int body_chunk_size;
9543
9544   if (!(file = fopen(filename, MODE_WRITE)))
9545   {
9546     Warn("cannot save level recording file '%s'", filename);
9547
9548     return;
9549   }
9550
9551   tape_pos_size = getTapePosSize(&tape);
9552
9553   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9554   body_chunk_size = tape_pos_size * tape.length;
9555
9556   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9557   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9558
9559   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9560   SaveTape_VERS(file, &tape);
9561
9562   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9563   SaveTape_HEAD(file, &tape);
9564
9565   if (checkSaveTape_SCRN(&tape))
9566   {
9567     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9568     SaveTape_SCRN(file, &tape);
9569   }
9570
9571   putFileChunkBE(file, "INFO", info_chunk_size);
9572   SaveTape_INFO(file, &tape);
9573
9574   putFileChunkBE(file, "BODY", body_chunk_size);
9575   SaveTape_BODY(file, &tape);
9576
9577   fclose(file);
9578
9579   SetFilePermissions(filename, PERMS_PRIVATE);
9580 }
9581
9582 static void SaveTapeExt(char *filename)
9583 {
9584   int i;
9585
9586   tape.file_version = FILE_VERSION_ACTUAL;
9587   tape.game_version = GAME_VERSION_ACTUAL;
9588
9589   tape.num_participating_players = 0;
9590
9591   // count number of participating players
9592   for (i = 0; i < MAX_PLAYERS; i++)
9593     if (tape.player_participates[i])
9594       tape.num_participating_players++;
9595
9596   SaveTapeToFilename(filename);
9597
9598   tape.changed = FALSE;
9599 }
9600
9601 void SaveTape(int nr)
9602 {
9603   char *filename = getTapeFilename(nr);
9604
9605   InitTapeDirectory(leveldir_current->subdir);
9606
9607   SaveTapeExt(filename);
9608 }
9609
9610 void SaveScoreTape(int nr)
9611 {
9612   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9613
9614   // used instead of "leveldir_current->subdir" (for network games)
9615   InitScoreTapeDirectory(levelset.identifier, nr);
9616
9617   SaveTapeExt(filename);
9618 }
9619
9620 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9621                                   unsigned int req_state_added)
9622 {
9623   char *filename = getTapeFilename(nr);
9624   boolean new_tape = !fileExists(filename);
9625   boolean tape_saved = FALSE;
9626
9627   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9628   {
9629     SaveTape(nr);
9630
9631     if (new_tape)
9632       Request(msg_saved, REQ_CONFIRM | req_state_added);
9633
9634     tape_saved = TRUE;
9635   }
9636
9637   return tape_saved;
9638 }
9639
9640 boolean SaveTapeChecked(int nr)
9641 {
9642   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9643 }
9644
9645 boolean SaveTapeChecked_LevelSolved(int nr)
9646 {
9647   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9648                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9649 }
9650
9651 void DumpTape(struct TapeInfo *tape)
9652 {
9653   int tape_frame_counter;
9654   int i, j;
9655
9656   if (tape->no_valid_file)
9657   {
9658     Warn("cannot dump -- no valid tape file found");
9659
9660     return;
9661   }
9662
9663   PrintLine("-", 79);
9664
9665   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9666         tape->level_nr, tape->file_version, tape->game_version);
9667   Print("                  (effective engine version %08d)\n",
9668         tape->engine_version);
9669   Print("Level series identifier: '%s'\n", tape->level_identifier);
9670
9671   Print("Solution tape: %s\n",
9672         tape->solved ? "yes" :
9673         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9674
9675   Print("Special tape properties: ");
9676   if (tape->property_bits == TAPE_PROPERTY_NONE)
9677     Print("[none]");
9678   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9679     Print("[em_random_bug]");
9680   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9681     Print("[game_speed]");
9682   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9683     Print("[pause]");
9684   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9685     Print("[single_step]");
9686   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9687     Print("[snapshot]");
9688   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9689     Print("[replayed]");
9690   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9691     Print("[tas_keys]");
9692   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9693     Print("[small_graphics]");
9694   Print("\n");
9695
9696   int year2 = tape->date / 10000;
9697   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9698   int month_index_raw = (tape->date / 100) % 100;
9699   int month_index = month_index_raw % 12;       // prevent invalid index
9700   int month = month_index + 1;
9701   int day = tape->date % 100;
9702
9703   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9704
9705   PrintLine("-", 79);
9706
9707   tape_frame_counter = 0;
9708
9709   for (i = 0; i < tape->length; i++)
9710   {
9711     if (i >= MAX_TAPE_LEN)
9712       break;
9713
9714     Print("%04d: ", i);
9715
9716     for (j = 0; j < MAX_PLAYERS; j++)
9717     {
9718       if (tape->player_participates[j])
9719       {
9720         int action = tape->pos[i].action[j];
9721
9722         Print("%d:%02x ", j, action);
9723         Print("[%c%c%c%c|%c%c] - ",
9724               (action & JOY_LEFT ? '<' : ' '),
9725               (action & JOY_RIGHT ? '>' : ' '),
9726               (action & JOY_UP ? '^' : ' '),
9727               (action & JOY_DOWN ? 'v' : ' '),
9728               (action & JOY_BUTTON_1 ? '1' : ' '),
9729               (action & JOY_BUTTON_2 ? '2' : ' '));
9730       }
9731     }
9732
9733     Print("(%03d) ", tape->pos[i].delay);
9734     Print("[%05d]\n", tape_frame_counter);
9735
9736     tape_frame_counter += tape->pos[i].delay;
9737   }
9738
9739   PrintLine("-", 79);
9740 }
9741
9742 void DumpTapes(void)
9743 {
9744   static LevelDirTree *dumptape_leveldir = NULL;
9745
9746   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9747                                                 global.dumptape_leveldir);
9748
9749   if (dumptape_leveldir == NULL)
9750     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9751
9752   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9753       global.dumptape_level_nr > dumptape_leveldir->last_level)
9754     Fail("no such level number: %d", global.dumptape_level_nr);
9755
9756   leveldir_current = dumptape_leveldir;
9757
9758   if (options.mytapes)
9759     LoadTape(global.dumptape_level_nr);
9760   else
9761     LoadSolutionTape(global.dumptape_level_nr);
9762
9763   DumpTape(&tape);
9764
9765   CloseAllAndExit(0);
9766 }
9767
9768
9769 // ============================================================================
9770 // score file functions
9771 // ============================================================================
9772
9773 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9774 {
9775   int i;
9776
9777   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9778   {
9779     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9780     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9781     scores->entry[i].score = 0;
9782     scores->entry[i].time = 0;
9783
9784     scores->entry[i].id = -1;
9785     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9786     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9787     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9788     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9789     strcpy(scores->entry[i].country_code, "??");
9790   }
9791
9792   scores->num_entries = 0;
9793   scores->last_added = -1;
9794   scores->last_added_local = -1;
9795
9796   scores->updated = FALSE;
9797   scores->uploaded = FALSE;
9798   scores->tape_downloaded = FALSE;
9799   scores->force_last_added = FALSE;
9800
9801   // The following values are intentionally not reset here:
9802   // - last_level_nr
9803   // - last_entry_nr
9804   // - next_level_nr
9805   // - continue_playing
9806   // - continue_on_return
9807 }
9808
9809 static void setScoreInfoToDefaults(void)
9810 {
9811   setScoreInfoToDefaultsExt(&scores);
9812 }
9813
9814 static void setServerScoreInfoToDefaults(void)
9815 {
9816   setScoreInfoToDefaultsExt(&server_scores);
9817 }
9818
9819 static void LoadScore_OLD(int nr)
9820 {
9821   int i;
9822   char *filename = getScoreFilename(nr);
9823   char cookie[MAX_LINE_LEN];
9824   char line[MAX_LINE_LEN];
9825   char *line_ptr;
9826   FILE *file;
9827
9828   if (!(file = fopen(filename, MODE_READ)))
9829     return;
9830
9831   // check file identifier
9832   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9833     cookie[0] = '\0';
9834   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9835     cookie[strlen(cookie) - 1] = '\0';
9836
9837   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9838   {
9839     Warn("unknown format of score file '%s'", filename);
9840
9841     fclose(file);
9842
9843     return;
9844   }
9845
9846   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9847   {
9848     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9849       Warn("fscanf() failed; %s", strerror(errno));
9850
9851     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9852       line[0] = '\0';
9853
9854     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9855       line[strlen(line) - 1] = '\0';
9856
9857     for (line_ptr = line; *line_ptr; line_ptr++)
9858     {
9859       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9860       {
9861         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9862         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9863         break;
9864       }
9865     }
9866   }
9867
9868   fclose(file);
9869 }
9870
9871 static void ConvertScore_OLD(void)
9872 {
9873   // only convert score to time for levels that rate playing time over score
9874   if (!level.rate_time_over_score)
9875     return;
9876
9877   // convert old score to playing time for score-less levels (like Supaplex)
9878   int time_final_max = 999;
9879   int i;
9880
9881   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9882   {
9883     int score = scores.entry[i].score;
9884
9885     if (score > 0 && score < time_final_max)
9886       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9887   }
9888 }
9889
9890 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9891 {
9892   scores->file_version = getFileVersion(file);
9893   scores->game_version = getFileVersion(file);
9894
9895   return chunk_size;
9896 }
9897
9898 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9899 {
9900   char *level_identifier = NULL;
9901   int level_identifier_size;
9902   int i;
9903
9904   level_identifier_size = getFile16BitBE(file);
9905
9906   level_identifier = checked_malloc(level_identifier_size);
9907
9908   for (i = 0; i < level_identifier_size; i++)
9909     level_identifier[i] = getFile8Bit(file);
9910
9911   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9912   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9913
9914   checked_free(level_identifier);
9915
9916   scores->level_nr = getFile16BitBE(file);
9917   scores->num_entries = getFile16BitBE(file);
9918
9919   chunk_size = 2 + level_identifier_size + 2 + 2;
9920
9921   return chunk_size;
9922 }
9923
9924 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9925 {
9926   int i, j;
9927
9928   for (i = 0; i < scores->num_entries; i++)
9929   {
9930     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9931       scores->entry[i].name[j] = getFile8Bit(file);
9932
9933     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9934   }
9935
9936   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9937
9938   return chunk_size;
9939 }
9940
9941 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9942 {
9943   int i;
9944
9945   for (i = 0; i < scores->num_entries; i++)
9946     scores->entry[i].score = getFile16BitBE(file);
9947
9948   chunk_size = scores->num_entries * 2;
9949
9950   return chunk_size;
9951 }
9952
9953 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9954 {
9955   int i;
9956
9957   for (i = 0; i < scores->num_entries; i++)
9958     scores->entry[i].score = getFile32BitBE(file);
9959
9960   chunk_size = scores->num_entries * 4;
9961
9962   return chunk_size;
9963 }
9964
9965 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9966 {
9967   int i;
9968
9969   for (i = 0; i < scores->num_entries; i++)
9970     scores->entry[i].time = getFile32BitBE(file);
9971
9972   chunk_size = scores->num_entries * 4;
9973
9974   return chunk_size;
9975 }
9976
9977 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9978 {
9979   int i, j;
9980
9981   for (i = 0; i < scores->num_entries; i++)
9982   {
9983     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9984       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9985
9986     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9987   }
9988
9989   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9990
9991   return chunk_size;
9992 }
9993
9994 void LoadScore(int nr)
9995 {
9996   char *filename = getScoreFilename(nr);
9997   char cookie[MAX_LINE_LEN];
9998   char chunk_name[CHUNK_ID_LEN + 1];
9999   int chunk_size;
10000   boolean old_score_file_format = FALSE;
10001   File *file;
10002
10003   // always start with reliable default values
10004   setScoreInfoToDefaults();
10005
10006   if (!(file = openFile(filename, MODE_READ)))
10007     return;
10008
10009   getFileChunkBE(file, chunk_name, NULL);
10010   if (strEqual(chunk_name, "RND1"))
10011   {
10012     getFile32BitBE(file);               // not used
10013
10014     getFileChunkBE(file, chunk_name, NULL);
10015     if (!strEqual(chunk_name, "SCOR"))
10016     {
10017       Warn("unknown format of score file '%s'", filename);
10018
10019       closeFile(file);
10020
10021       return;
10022     }
10023   }
10024   else  // check for old file format with cookie string
10025   {
10026     strcpy(cookie, chunk_name);
10027     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10028       cookie[4] = '\0';
10029     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10030       cookie[strlen(cookie) - 1] = '\0';
10031
10032     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10033     {
10034       Warn("unknown format of score file '%s'", filename);
10035
10036       closeFile(file);
10037
10038       return;
10039     }
10040
10041     old_score_file_format = TRUE;
10042   }
10043
10044   if (old_score_file_format)
10045   {
10046     // score files from versions before 4.2.4.0 without chunk structure
10047     LoadScore_OLD(nr);
10048
10049     // convert score to time, if possible (mainly for Supaplex levels)
10050     ConvertScore_OLD();
10051   }
10052   else
10053   {
10054     static struct
10055     {
10056       char *name;
10057       int size;
10058       int (*loader)(File *, int, struct ScoreInfo *);
10059     }
10060     chunk_info[] =
10061     {
10062       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10063       { "INFO", -1,                     LoadScore_INFO },
10064       { "NAME", -1,                     LoadScore_NAME },
10065       { "SCOR", -1,                     LoadScore_SCOR },
10066       { "SC4R", -1,                     LoadScore_SC4R },
10067       { "TIME", -1,                     LoadScore_TIME },
10068       { "TAPE", -1,                     LoadScore_TAPE },
10069
10070       {  NULL,  0,                      NULL }
10071     };
10072
10073     while (getFileChunkBE(file, chunk_name, &chunk_size))
10074     {
10075       int i = 0;
10076
10077       while (chunk_info[i].name != NULL &&
10078              !strEqual(chunk_name, chunk_info[i].name))
10079         i++;
10080
10081       if (chunk_info[i].name == NULL)
10082       {
10083         Warn("unknown chunk '%s' in score file '%s'",
10084               chunk_name, filename);
10085
10086         ReadUnusedBytesFromFile(file, chunk_size);
10087       }
10088       else if (chunk_info[i].size != -1 &&
10089                chunk_info[i].size != chunk_size)
10090       {
10091         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10092               chunk_size, chunk_name, filename);
10093
10094         ReadUnusedBytesFromFile(file, chunk_size);
10095       }
10096       else
10097       {
10098         // call function to load this score chunk
10099         int chunk_size_expected =
10100           (chunk_info[i].loader)(file, chunk_size, &scores);
10101
10102         // the size of some chunks cannot be checked before reading other
10103         // chunks first (like "HEAD" and "BODY") that contain some header
10104         // information, so check them here
10105         if (chunk_size_expected != chunk_size)
10106         {
10107           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10108                 chunk_size, chunk_name, filename);
10109         }
10110       }
10111     }
10112   }
10113
10114   closeFile(file);
10115 }
10116
10117 #if ENABLE_HISTORIC_CHUNKS
10118 void SaveScore_OLD(int nr)
10119 {
10120   int i;
10121   char *filename = getScoreFilename(nr);
10122   FILE *file;
10123
10124   // used instead of "leveldir_current->subdir" (for network games)
10125   InitScoreDirectory(levelset.identifier);
10126
10127   if (!(file = fopen(filename, MODE_WRITE)))
10128   {
10129     Warn("cannot save score for level %d", nr);
10130
10131     return;
10132   }
10133
10134   fprintf(file, "%s\n\n", SCORE_COOKIE);
10135
10136   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10137     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10138
10139   fclose(file);
10140
10141   SetFilePermissions(filename, PERMS_PRIVATE);
10142 }
10143 #endif
10144
10145 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10146 {
10147   putFileVersion(file, scores->file_version);
10148   putFileVersion(file, scores->game_version);
10149 }
10150
10151 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10152 {
10153   int level_identifier_size = strlen(scores->level_identifier) + 1;
10154   int i;
10155
10156   putFile16BitBE(file, level_identifier_size);
10157
10158   for (i = 0; i < level_identifier_size; i++)
10159     putFile8Bit(file, scores->level_identifier[i]);
10160
10161   putFile16BitBE(file, scores->level_nr);
10162   putFile16BitBE(file, scores->num_entries);
10163 }
10164
10165 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10166 {
10167   int i, j;
10168
10169   for (i = 0; i < scores->num_entries; i++)
10170   {
10171     int name_size = strlen(scores->entry[i].name);
10172
10173     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10174       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10175   }
10176 }
10177
10178 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10179 {
10180   int i;
10181
10182   for (i = 0; i < scores->num_entries; i++)
10183     putFile16BitBE(file, scores->entry[i].score);
10184 }
10185
10186 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10187 {
10188   int i;
10189
10190   for (i = 0; i < scores->num_entries; i++)
10191     putFile32BitBE(file, scores->entry[i].score);
10192 }
10193
10194 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10195 {
10196   int i;
10197
10198   for (i = 0; i < scores->num_entries; i++)
10199     putFile32BitBE(file, scores->entry[i].time);
10200 }
10201
10202 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10203 {
10204   int i, j;
10205
10206   for (i = 0; i < scores->num_entries; i++)
10207   {
10208     int size = strlen(scores->entry[i].tape_basename);
10209
10210     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10211       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10212   }
10213 }
10214
10215 static void SaveScoreToFilename(char *filename)
10216 {
10217   FILE *file;
10218   int info_chunk_size;
10219   int name_chunk_size;
10220   int scor_chunk_size;
10221   int sc4r_chunk_size;
10222   int time_chunk_size;
10223   int tape_chunk_size;
10224   boolean has_large_score_values;
10225   int i;
10226
10227   if (!(file = fopen(filename, MODE_WRITE)))
10228   {
10229     Warn("cannot save score file '%s'", filename);
10230
10231     return;
10232   }
10233
10234   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10235   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10236   scor_chunk_size = scores.num_entries * 2;
10237   sc4r_chunk_size = scores.num_entries * 4;
10238   time_chunk_size = scores.num_entries * 4;
10239   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10240
10241   has_large_score_values = FALSE;
10242   for (i = 0; i < scores.num_entries; i++)
10243     if (scores.entry[i].score > 0xffff)
10244       has_large_score_values = TRUE;
10245
10246   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10247   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10248
10249   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10250   SaveScore_VERS(file, &scores);
10251
10252   putFileChunkBE(file, "INFO", info_chunk_size);
10253   SaveScore_INFO(file, &scores);
10254
10255   putFileChunkBE(file, "NAME", name_chunk_size);
10256   SaveScore_NAME(file, &scores);
10257
10258   if (has_large_score_values)
10259   {
10260     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10261     SaveScore_SC4R(file, &scores);
10262   }
10263   else
10264   {
10265     putFileChunkBE(file, "SCOR", scor_chunk_size);
10266     SaveScore_SCOR(file, &scores);
10267   }
10268
10269   putFileChunkBE(file, "TIME", time_chunk_size);
10270   SaveScore_TIME(file, &scores);
10271
10272   putFileChunkBE(file, "TAPE", tape_chunk_size);
10273   SaveScore_TAPE(file, &scores);
10274
10275   fclose(file);
10276
10277   SetFilePermissions(filename, PERMS_PRIVATE);
10278 }
10279
10280 void SaveScore(int nr)
10281 {
10282   char *filename = getScoreFilename(nr);
10283   int i;
10284
10285   // used instead of "leveldir_current->subdir" (for network games)
10286   InitScoreDirectory(levelset.identifier);
10287
10288   scores.file_version = FILE_VERSION_ACTUAL;
10289   scores.game_version = GAME_VERSION_ACTUAL;
10290
10291   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10292   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10293   scores.level_nr = level_nr;
10294
10295   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10296     if (scores.entry[i].score == 0 &&
10297         scores.entry[i].time == 0 &&
10298         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10299       break;
10300
10301   scores.num_entries = i;
10302
10303   if (scores.num_entries == 0)
10304     return;
10305
10306   SaveScoreToFilename(filename);
10307 }
10308
10309 static void LoadServerScoreFromCache(int nr)
10310 {
10311   struct ScoreEntry score_entry;
10312   struct
10313   {
10314     void *value;
10315     boolean is_string;
10316     int string_size;
10317   }
10318   score_mapping[] =
10319   {
10320     { &score_entry.score,               FALSE,  0                       },
10321     { &score_entry.time,                FALSE,  0                       },
10322     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10323     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10324     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10325     { &score_entry.id,                  FALSE,  0                       },
10326     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10327     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10328     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10329     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10330
10331     { NULL,                             FALSE,  0                       }
10332   };
10333   char *filename = getScoreCacheFilename(nr);
10334   SetupFileHash *score_hash = loadSetupFileHash(filename);
10335   int i, j;
10336
10337   server_scores.num_entries = 0;
10338
10339   if (score_hash == NULL)
10340     return;
10341
10342   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10343   {
10344     score_entry = server_scores.entry[i];
10345
10346     for (j = 0; score_mapping[j].value != NULL; j++)
10347     {
10348       char token[10];
10349
10350       sprintf(token, "%02d.%d", i, j);
10351
10352       char *value = getHashEntry(score_hash, token);
10353
10354       if (value == NULL)
10355         continue;
10356
10357       if (score_mapping[j].is_string)
10358       {
10359         char *score_value = (char *)score_mapping[j].value;
10360         int value_size = score_mapping[j].string_size;
10361
10362         strncpy(score_value, value, value_size);
10363         score_value[value_size] = '\0';
10364       }
10365       else
10366       {
10367         int *score_value = (int *)score_mapping[j].value;
10368
10369         *score_value = atoi(value);
10370       }
10371
10372       server_scores.num_entries = i + 1;
10373     }
10374
10375     server_scores.entry[i] = score_entry;
10376   }
10377
10378   freeSetupFileHash(score_hash);
10379 }
10380
10381 void LoadServerScore(int nr, boolean download_score)
10382 {
10383   if (!setup.use_api_server)
10384     return;
10385
10386   // always start with reliable default values
10387   setServerScoreInfoToDefaults();
10388
10389   // 1st step: load server scores from cache file (which may not exist)
10390   // (this should prevent reading it while the thread is writing to it)
10391   LoadServerScoreFromCache(nr);
10392
10393   if (download_score && runtime.use_api_server)
10394   {
10395     // 2nd step: download server scores from score server to cache file
10396     // (as thread, as it might time out if the server is not reachable)
10397     ApiGetScoreAsThread(nr);
10398   }
10399 }
10400
10401 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10402 {
10403   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10404
10405   // if score tape not uploaded, ask for uploading missing tapes later
10406   if (!setup.has_remaining_tapes)
10407     setup.ask_for_remaining_tapes = TRUE;
10408
10409   setup.provide_uploading_tapes = TRUE;
10410   setup.has_remaining_tapes = TRUE;
10411
10412   SaveSetup_ServerSetup();
10413 }
10414
10415 void SaveServerScore(int nr, boolean tape_saved)
10416 {
10417   if (!runtime.use_api_server)
10418   {
10419     PrepareScoreTapesForUpload(leveldir_current->subdir);
10420
10421     return;
10422   }
10423
10424   ApiAddScoreAsThread(nr, tape_saved, NULL);
10425 }
10426
10427 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10428                              char *score_tape_filename)
10429 {
10430   if (!runtime.use_api_server)
10431     return;
10432
10433   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10434 }
10435
10436 void LoadLocalAndServerScore(int nr, boolean download_score)
10437 {
10438   int last_added_local = scores.last_added_local;
10439   boolean force_last_added = scores.force_last_added;
10440
10441   // needed if only showing server scores
10442   setScoreInfoToDefaults();
10443
10444   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10445     LoadScore(nr);
10446
10447   // restore last added local score entry (before merging server scores)
10448   scores.last_added = scores.last_added_local = last_added_local;
10449
10450   if (setup.use_api_server &&
10451       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10452   {
10453     // load server scores from cache file and trigger update from server
10454     LoadServerScore(nr, download_score);
10455
10456     // merge local scores with scores from server
10457     MergeServerScore();
10458   }
10459
10460   if (force_last_added)
10461     scores.force_last_added = force_last_added;
10462 }
10463
10464
10465 // ============================================================================
10466 // setup file functions
10467 // ============================================================================
10468
10469 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10470
10471
10472 static struct TokenInfo global_setup_tokens[] =
10473 {
10474   {
10475     TYPE_STRING,
10476     &setup.player_name,                         "player_name"
10477   },
10478   {
10479     TYPE_SWITCH,
10480     &setup.multiple_users,                      "multiple_users"
10481   },
10482   {
10483     TYPE_SWITCH,
10484     &setup.sound,                               "sound"
10485   },
10486   {
10487     TYPE_SWITCH,
10488     &setup.sound_loops,                         "repeating_sound_loops"
10489   },
10490   {
10491     TYPE_SWITCH,
10492     &setup.sound_music,                         "background_music"
10493   },
10494   {
10495     TYPE_SWITCH,
10496     &setup.sound_simple,                        "simple_sound_effects"
10497   },
10498   {
10499     TYPE_SWITCH,
10500     &setup.toons,                               "toons"
10501   },
10502   {
10503     TYPE_SWITCH,
10504     &setup.global_animations,                   "global_animations"
10505   },
10506   {
10507     TYPE_SWITCH,
10508     &setup.scroll_delay,                        "scroll_delay"
10509   },
10510   {
10511     TYPE_SWITCH,
10512     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10513   },
10514   {
10515     TYPE_INTEGER,
10516     &setup.scroll_delay_value,                  "scroll_delay_value"
10517   },
10518   {
10519     TYPE_STRING,
10520     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10521   },
10522   {
10523     TYPE_INTEGER,
10524     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10525   },
10526   {
10527     TYPE_SWITCH,
10528     &setup.fade_screens,                        "fade_screens"
10529   },
10530   {
10531     TYPE_SWITCH,
10532     &setup.autorecord,                          "automatic_tape_recording"
10533   },
10534   {
10535     TYPE_SWITCH,
10536     &setup.autorecord_after_replay,             "autorecord_after_replay"
10537   },
10538   {
10539     TYPE_SWITCH,
10540     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10541   },
10542   {
10543     TYPE_SWITCH,
10544     &setup.show_titlescreen,                    "show_titlescreen"
10545   },
10546   {
10547     TYPE_SWITCH,
10548     &setup.quick_doors,                         "quick_doors"
10549   },
10550   {
10551     TYPE_SWITCH,
10552     &setup.team_mode,                           "team_mode"
10553   },
10554   {
10555     TYPE_SWITCH,
10556     &setup.handicap,                            "handicap"
10557   },
10558   {
10559     TYPE_SWITCH,
10560     &setup.skip_levels,                         "skip_levels"
10561   },
10562   {
10563     TYPE_SWITCH,
10564     &setup.increment_levels,                    "increment_levels"
10565   },
10566   {
10567     TYPE_SWITCH,
10568     &setup.auto_play_next_level,                "auto_play_next_level"
10569   },
10570   {
10571     TYPE_SWITCH,
10572     &setup.count_score_after_game,              "count_score_after_game"
10573   },
10574   {
10575     TYPE_SWITCH,
10576     &setup.show_scores_after_game,              "show_scores_after_game"
10577   },
10578   {
10579     TYPE_SWITCH,
10580     &setup.time_limit,                          "time_limit"
10581   },
10582   {
10583     TYPE_SWITCH,
10584     &setup.fullscreen,                          "fullscreen"
10585   },
10586   {
10587     TYPE_INTEGER,
10588     &setup.window_scaling_percent,              "window_scaling_percent"
10589   },
10590   {
10591     TYPE_STRING,
10592     &setup.window_scaling_quality,              "window_scaling_quality"
10593   },
10594   {
10595     TYPE_STRING,
10596     &setup.screen_rendering_mode,               "screen_rendering_mode"
10597   },
10598   {
10599     TYPE_STRING,
10600     &setup.vsync_mode,                          "vsync_mode"
10601   },
10602   {
10603     TYPE_SWITCH,
10604     &setup.ask_on_escape,                       "ask_on_escape"
10605   },
10606   {
10607     TYPE_SWITCH,
10608     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10609   },
10610   {
10611     TYPE_SWITCH,
10612     &setup.ask_on_game_over,                    "ask_on_game_over"
10613   },
10614   {
10615     TYPE_SWITCH,
10616     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10617   },
10618   {
10619     TYPE_SWITCH,
10620     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10621   },
10622   {
10623     TYPE_SWITCH,
10624     &setup.quick_switch,                        "quick_player_switch"
10625   },
10626   {
10627     TYPE_SWITCH,
10628     &setup.input_on_focus,                      "input_on_focus"
10629   },
10630   {
10631     TYPE_SWITCH,
10632     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10633   },
10634   {
10635     TYPE_SWITCH,
10636     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10637   },
10638   {
10639     TYPE_SWITCH,
10640     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10641   },
10642   {
10643     TYPE_SWITCH,
10644     &setup.game_speed_extended,                 "game_speed_extended"
10645   },
10646   {
10647     TYPE_INTEGER,
10648     &setup.game_frame_delay,                    "game_frame_delay"
10649   },
10650   {
10651     TYPE_SWITCH,
10652     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10653   },
10654   {
10655     TYPE_SWITCH,
10656     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10657   },
10658   {
10659     TYPE_SWITCH,
10660     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10661   },
10662   {
10663     TYPE_SWITCH3,
10664     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10665   },
10666   {
10667     TYPE_SWITCH,
10668     &setup.sp_show_border_elements,             "sp_show_border_elements"
10669   },
10670   {
10671     TYPE_SWITCH,
10672     &setup.small_game_graphics,                 "small_game_graphics"
10673   },
10674   {
10675     TYPE_SWITCH,
10676     &setup.show_load_save_buttons,              "show_load_save_buttons"
10677   },
10678   {
10679     TYPE_SWITCH,
10680     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10681   },
10682   {
10683     TYPE_STRING,
10684     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10685   },
10686   {
10687     TYPE_STRING,
10688     &setup.graphics_set,                        "graphics_set"
10689   },
10690   {
10691     TYPE_STRING,
10692     &setup.sounds_set,                          "sounds_set"
10693   },
10694   {
10695     TYPE_STRING,
10696     &setup.music_set,                           "music_set"
10697   },
10698   {
10699     TYPE_SWITCH3,
10700     &setup.override_level_graphics,             "override_level_graphics"
10701   },
10702   {
10703     TYPE_SWITCH3,
10704     &setup.override_level_sounds,               "override_level_sounds"
10705   },
10706   {
10707     TYPE_SWITCH3,
10708     &setup.override_level_music,                "override_level_music"
10709   },
10710   {
10711     TYPE_INTEGER,
10712     &setup.volume_simple,                       "volume_simple"
10713   },
10714   {
10715     TYPE_INTEGER,
10716     &setup.volume_loops,                        "volume_loops"
10717   },
10718   {
10719     TYPE_INTEGER,
10720     &setup.volume_music,                        "volume_music"
10721   },
10722   {
10723     TYPE_SWITCH,
10724     &setup.network_mode,                        "network_mode"
10725   },
10726   {
10727     TYPE_PLAYER,
10728     &setup.network_player_nr,                   "network_player"
10729   },
10730   {
10731     TYPE_STRING,
10732     &setup.network_server_hostname,             "network_server_hostname"
10733   },
10734   {
10735     TYPE_STRING,
10736     &setup.touch.control_type,                  "touch.control_type"
10737   },
10738   {
10739     TYPE_INTEGER,
10740     &setup.touch.move_distance,                 "touch.move_distance"
10741   },
10742   {
10743     TYPE_INTEGER,
10744     &setup.touch.drop_distance,                 "touch.drop_distance"
10745   },
10746   {
10747     TYPE_INTEGER,
10748     &setup.touch.transparency,                  "touch.transparency"
10749   },
10750   {
10751     TYPE_INTEGER,
10752     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10753   },
10754   {
10755     TYPE_INTEGER,
10756     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10757   },
10758   {
10759     TYPE_INTEGER,
10760     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10761   },
10762   {
10763     TYPE_INTEGER,
10764     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10765   },
10766   {
10767     TYPE_INTEGER,
10768     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10769   },
10770   {
10771     TYPE_INTEGER,
10772     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10773   },
10774   {
10775     TYPE_SWITCH,
10776     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10777   },
10778 };
10779
10780 static struct TokenInfo auto_setup_tokens[] =
10781 {
10782   {
10783     TYPE_INTEGER,
10784     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10785   },
10786 };
10787
10788 static struct TokenInfo server_setup_tokens[] =
10789 {
10790   {
10791     TYPE_STRING,
10792     &setup.player_uuid,                         "player_uuid"
10793   },
10794   {
10795     TYPE_INTEGER,
10796     &setup.player_version,                      "player_version"
10797   },
10798   {
10799     TYPE_SWITCH,
10800     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10801   },
10802   {
10803     TYPE_STRING,
10804     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10805   },
10806   {
10807     TYPE_STRING,
10808     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10809   },
10810   {
10811     TYPE_SWITCH,
10812     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10813   },
10814   {
10815     TYPE_SWITCH,
10816     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10817   },
10818   {
10819     TYPE_SWITCH,
10820     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10821   },
10822   {
10823     TYPE_SWITCH,
10824     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10825   },
10826   {
10827     TYPE_SWITCH,
10828     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10829   },
10830 };
10831
10832 static struct TokenInfo editor_setup_tokens[] =
10833 {
10834   {
10835     TYPE_SWITCH,
10836     &setup.editor.el_classic,                   "editor.el_classic"
10837   },
10838   {
10839     TYPE_SWITCH,
10840     &setup.editor.el_custom,                    "editor.el_custom"
10841   },
10842   {
10843     TYPE_SWITCH,
10844     &setup.editor.el_user_defined,              "editor.el_user_defined"
10845   },
10846   {
10847     TYPE_SWITCH,
10848     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10849   },
10850   {
10851     TYPE_SWITCH,
10852     &setup.editor.el_headlines,                 "editor.el_headlines"
10853   },
10854   {
10855     TYPE_SWITCH,
10856     &setup.editor.show_element_token,           "editor.show_element_token"
10857   },
10858   {
10859     TYPE_SWITCH,
10860     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10861   },
10862 };
10863
10864 static struct TokenInfo editor_cascade_setup_tokens[] =
10865 {
10866   {
10867     TYPE_SWITCH,
10868     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10869   },
10870   {
10871     TYPE_SWITCH,
10872     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10873   },
10874   {
10875     TYPE_SWITCH,
10876     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10877   },
10878   {
10879     TYPE_SWITCH,
10880     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10881   },
10882   {
10883     TYPE_SWITCH,
10884     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10885   },
10886   {
10887     TYPE_SWITCH,
10888     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10889   },
10890   {
10891     TYPE_SWITCH,
10892     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10893   },
10894   {
10895     TYPE_SWITCH,
10896     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10897   },
10898   {
10899     TYPE_SWITCH,
10900     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10901   },
10902   {
10903     TYPE_SWITCH,
10904     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10905   },
10906   {
10907     TYPE_SWITCH,
10908     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10909   },
10910   {
10911     TYPE_SWITCH,
10912     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10913   },
10914   {
10915     TYPE_SWITCH,
10916     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10917   },
10918   {
10919     TYPE_SWITCH,
10920     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10921   },
10922   {
10923     TYPE_SWITCH,
10924     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10925   },
10926   {
10927     TYPE_SWITCH,
10928     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10929   },
10930   {
10931     TYPE_SWITCH,
10932     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10933   },
10934   {
10935     TYPE_SWITCH,
10936     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10937   },
10938   {
10939     TYPE_SWITCH,
10940     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10941   },
10942   {
10943     TYPE_SWITCH,
10944     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10945   },
10946 };
10947
10948 static struct TokenInfo shortcut_setup_tokens[] =
10949 {
10950   {
10951     TYPE_KEY_X11,
10952     &setup.shortcut.save_game,                  "shortcut.save_game"
10953   },
10954   {
10955     TYPE_KEY_X11,
10956     &setup.shortcut.load_game,                  "shortcut.load_game"
10957   },
10958   {
10959     TYPE_KEY_X11,
10960     &setup.shortcut.restart_game,               "shortcut.restart_game"
10961   },
10962   {
10963     TYPE_KEY_X11,
10964     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10965   },
10966   {
10967     TYPE_KEY_X11,
10968     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10969   },
10970   {
10971     TYPE_KEY_X11,
10972     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10973   },
10974   {
10975     TYPE_KEY_X11,
10976     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10977   },
10978   {
10979     TYPE_KEY_X11,
10980     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10981   },
10982   {
10983     TYPE_KEY_X11,
10984     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10985   },
10986   {
10987     TYPE_KEY_X11,
10988     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10989   },
10990   {
10991     TYPE_KEY_X11,
10992     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10993   },
10994   {
10995     TYPE_KEY_X11,
10996     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10997   },
10998   {
10999     TYPE_KEY_X11,
11000     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11001   },
11002   {
11003     TYPE_KEY_X11,
11004     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11005   },
11006   {
11007     TYPE_KEY_X11,
11008     &setup.shortcut.tape_record,                "shortcut.tape_record"
11009   },
11010   {
11011     TYPE_KEY_X11,
11012     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11013   },
11014   {
11015     TYPE_KEY_X11,
11016     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11017   },
11018   {
11019     TYPE_KEY_X11,
11020     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11021   },
11022   {
11023     TYPE_KEY_X11,
11024     &setup.shortcut.sound_music,                "shortcut.sound_music"
11025   },
11026   {
11027     TYPE_KEY_X11,
11028     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11029   },
11030   {
11031     TYPE_KEY_X11,
11032     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11033   },
11034   {
11035     TYPE_KEY_X11,
11036     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11037   },
11038   {
11039     TYPE_KEY_X11,
11040     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11041   },
11042 };
11043
11044 static struct SetupInputInfo setup_input;
11045 static struct TokenInfo player_setup_tokens[] =
11046 {
11047   {
11048     TYPE_BOOLEAN,
11049     &setup_input.use_joystick,                  ".use_joystick"
11050   },
11051   {
11052     TYPE_STRING,
11053     &setup_input.joy.device_name,               ".joy.device_name"
11054   },
11055   {
11056     TYPE_INTEGER,
11057     &setup_input.joy.xleft,                     ".joy.xleft"
11058   },
11059   {
11060     TYPE_INTEGER,
11061     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11062   },
11063   {
11064     TYPE_INTEGER,
11065     &setup_input.joy.xright,                    ".joy.xright"
11066   },
11067   {
11068     TYPE_INTEGER,
11069     &setup_input.joy.yupper,                    ".joy.yupper"
11070   },
11071   {
11072     TYPE_INTEGER,
11073     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11074   },
11075   {
11076     TYPE_INTEGER,
11077     &setup_input.joy.ylower,                    ".joy.ylower"
11078   },
11079   {
11080     TYPE_INTEGER,
11081     &setup_input.joy.snap,                      ".joy.snap_field"
11082   },
11083   {
11084     TYPE_INTEGER,
11085     &setup_input.joy.drop,                      ".joy.place_bomb"
11086   },
11087   {
11088     TYPE_KEY_X11,
11089     &setup_input.key.left,                      ".key.move_left"
11090   },
11091   {
11092     TYPE_KEY_X11,
11093     &setup_input.key.right,                     ".key.move_right"
11094   },
11095   {
11096     TYPE_KEY_X11,
11097     &setup_input.key.up,                        ".key.move_up"
11098   },
11099   {
11100     TYPE_KEY_X11,
11101     &setup_input.key.down,                      ".key.move_down"
11102   },
11103   {
11104     TYPE_KEY_X11,
11105     &setup_input.key.snap,                      ".key.snap_field"
11106   },
11107   {
11108     TYPE_KEY_X11,
11109     &setup_input.key.drop,                      ".key.place_bomb"
11110   },
11111 };
11112
11113 static struct TokenInfo system_setup_tokens[] =
11114 {
11115   {
11116     TYPE_STRING,
11117     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11118   },
11119   {
11120     TYPE_STRING,
11121     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11122   },
11123   {
11124     TYPE_STRING,
11125     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11126   },
11127   {
11128     TYPE_INTEGER,
11129     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11130   },
11131 };
11132
11133 static struct TokenInfo internal_setup_tokens[] =
11134 {
11135   {
11136     TYPE_STRING,
11137     &setup.internal.program_title,              "program_title"
11138   },
11139   {
11140     TYPE_STRING,
11141     &setup.internal.program_version,            "program_version"
11142   },
11143   {
11144     TYPE_STRING,
11145     &setup.internal.program_author,             "program_author"
11146   },
11147   {
11148     TYPE_STRING,
11149     &setup.internal.program_email,              "program_email"
11150   },
11151   {
11152     TYPE_STRING,
11153     &setup.internal.program_website,            "program_website"
11154   },
11155   {
11156     TYPE_STRING,
11157     &setup.internal.program_copyright,          "program_copyright"
11158   },
11159   {
11160     TYPE_STRING,
11161     &setup.internal.program_company,            "program_company"
11162   },
11163   {
11164     TYPE_STRING,
11165     &setup.internal.program_icon_file,          "program_icon_file"
11166   },
11167   {
11168     TYPE_STRING,
11169     &setup.internal.default_graphics_set,       "default_graphics_set"
11170   },
11171   {
11172     TYPE_STRING,
11173     &setup.internal.default_sounds_set,         "default_sounds_set"
11174   },
11175   {
11176     TYPE_STRING,
11177     &setup.internal.default_music_set,          "default_music_set"
11178   },
11179   {
11180     TYPE_STRING,
11181     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11182   },
11183   {
11184     TYPE_STRING,
11185     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11186   },
11187   {
11188     TYPE_STRING,
11189     &setup.internal.fallback_music_file,        "fallback_music_file"
11190   },
11191   {
11192     TYPE_STRING,
11193     &setup.internal.default_level_series,       "default_level_series"
11194   },
11195   {
11196     TYPE_INTEGER,
11197     &setup.internal.default_window_width,       "default_window_width"
11198   },
11199   {
11200     TYPE_INTEGER,
11201     &setup.internal.default_window_height,      "default_window_height"
11202   },
11203   {
11204     TYPE_BOOLEAN,
11205     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11206   },
11207   {
11208     TYPE_BOOLEAN,
11209     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11210   },
11211   {
11212     TYPE_BOOLEAN,
11213     &setup.internal.create_user_levelset,       "create_user_levelset"
11214   },
11215   {
11216     TYPE_BOOLEAN,
11217     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11218   },
11219   {
11220     TYPE_BOOLEAN,
11221     &setup.internal.menu_game,                  "menu_game"
11222   },
11223   {
11224     TYPE_BOOLEAN,
11225     &setup.internal.menu_engines,               "menu_engines"
11226   },
11227   {
11228     TYPE_BOOLEAN,
11229     &setup.internal.menu_editor,                "menu_editor"
11230   },
11231   {
11232     TYPE_BOOLEAN,
11233     &setup.internal.menu_graphics,              "menu_graphics"
11234   },
11235   {
11236     TYPE_BOOLEAN,
11237     &setup.internal.menu_sound,                 "menu_sound"
11238   },
11239   {
11240     TYPE_BOOLEAN,
11241     &setup.internal.menu_artwork,               "menu_artwork"
11242   },
11243   {
11244     TYPE_BOOLEAN,
11245     &setup.internal.menu_input,                 "menu_input"
11246   },
11247   {
11248     TYPE_BOOLEAN,
11249     &setup.internal.menu_touch,                 "menu_touch"
11250   },
11251   {
11252     TYPE_BOOLEAN,
11253     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11254   },
11255   {
11256     TYPE_BOOLEAN,
11257     &setup.internal.menu_exit,                  "menu_exit"
11258   },
11259   {
11260     TYPE_BOOLEAN,
11261     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11262   },
11263   {
11264     TYPE_BOOLEAN,
11265     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11266   },
11267   {
11268     TYPE_BOOLEAN,
11269     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11270   },
11271   {
11272     TYPE_BOOLEAN,
11273     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11274   },
11275   {
11276     TYPE_BOOLEAN,
11277     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11278   },
11279   {
11280     TYPE_BOOLEAN,
11281     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11282   },
11283   {
11284     TYPE_BOOLEAN,
11285     &setup.internal.info_title,                 "info_title"
11286   },
11287   {
11288     TYPE_BOOLEAN,
11289     &setup.internal.info_elements,              "info_elements"
11290   },
11291   {
11292     TYPE_BOOLEAN,
11293     &setup.internal.info_music,                 "info_music"
11294   },
11295   {
11296     TYPE_BOOLEAN,
11297     &setup.internal.info_credits,               "info_credits"
11298   },
11299   {
11300     TYPE_BOOLEAN,
11301     &setup.internal.info_program,               "info_program"
11302   },
11303   {
11304     TYPE_BOOLEAN,
11305     &setup.internal.info_version,               "info_version"
11306   },
11307   {
11308     TYPE_BOOLEAN,
11309     &setup.internal.info_levelset,              "info_levelset"
11310   },
11311   {
11312     TYPE_BOOLEAN,
11313     &setup.internal.info_exit,                  "info_exit"
11314   },
11315 };
11316
11317 static struct TokenInfo debug_setup_tokens[] =
11318 {
11319   {
11320     TYPE_INTEGER,
11321     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11322   },
11323   {
11324     TYPE_INTEGER,
11325     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11326   },
11327   {
11328     TYPE_INTEGER,
11329     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11330   },
11331   {
11332     TYPE_INTEGER,
11333     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11334   },
11335   {
11336     TYPE_INTEGER,
11337     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11338   },
11339   {
11340     TYPE_INTEGER,
11341     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11342   },
11343   {
11344     TYPE_INTEGER,
11345     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11346   },
11347   {
11348     TYPE_INTEGER,
11349     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11350   },
11351   {
11352     TYPE_INTEGER,
11353     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11354   },
11355   {
11356     TYPE_INTEGER,
11357     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11358   },
11359   {
11360     TYPE_KEY_X11,
11361     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11362   },
11363   {
11364     TYPE_KEY_X11,
11365     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11366   },
11367   {
11368     TYPE_KEY_X11,
11369     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11370   },
11371   {
11372     TYPE_KEY_X11,
11373     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11374   },
11375   {
11376     TYPE_KEY_X11,
11377     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11378   },
11379   {
11380     TYPE_KEY_X11,
11381     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11382   },
11383   {
11384     TYPE_KEY_X11,
11385     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11386   },
11387   {
11388     TYPE_KEY_X11,
11389     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11390   },
11391   {
11392     TYPE_KEY_X11,
11393     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11394   },
11395   {
11396     TYPE_KEY_X11,
11397     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11398   },
11399   {
11400     TYPE_BOOLEAN,
11401     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11402   {
11403     TYPE_BOOLEAN,
11404     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11405   },
11406   {
11407     TYPE_BOOLEAN,
11408     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11409   },
11410   {
11411     TYPE_SWITCH3,
11412     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11413   },
11414   {
11415     TYPE_INTEGER,
11416     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11417   },
11418 };
11419
11420 static struct TokenInfo options_setup_tokens[] =
11421 {
11422   {
11423     TYPE_BOOLEAN,
11424     &setup.options.verbose,                     "options.verbose"
11425   },
11426   {
11427     TYPE_BOOLEAN,
11428     &setup.options.debug,                       "options.debug"
11429   },
11430   {
11431     TYPE_STRING,
11432     &setup.options.debug_mode,                  "options.debug_mode"
11433   },
11434 };
11435
11436 static void setSetupInfoToDefaults(struct SetupInfo *si)
11437 {
11438   int i;
11439
11440   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11441
11442   si->multiple_users = TRUE;
11443
11444   si->sound = TRUE;
11445   si->sound_loops = TRUE;
11446   si->sound_music = TRUE;
11447   si->sound_simple = TRUE;
11448   si->toons = TRUE;
11449   si->global_animations = TRUE;
11450   si->scroll_delay = TRUE;
11451   si->forced_scroll_delay = FALSE;
11452   si->scroll_delay_value = STD_SCROLL_DELAY;
11453   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11454   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11455   si->fade_screens = TRUE;
11456   si->autorecord = TRUE;
11457   si->autorecord_after_replay = TRUE;
11458   si->auto_pause_on_start = FALSE;
11459   si->show_titlescreen = TRUE;
11460   si->quick_doors = FALSE;
11461   si->team_mode = FALSE;
11462   si->handicap = TRUE;
11463   si->skip_levels = TRUE;
11464   si->increment_levels = TRUE;
11465   si->auto_play_next_level = TRUE;
11466   si->count_score_after_game = TRUE;
11467   si->show_scores_after_game = TRUE;
11468   si->time_limit = TRUE;
11469   si->fullscreen = FALSE;
11470   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11471   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11472   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11473   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11474   si->ask_on_escape = TRUE;
11475   si->ask_on_escape_editor = TRUE;
11476   si->ask_on_game_over = TRUE;
11477   si->ask_on_quit_game = TRUE;
11478   si->ask_on_quit_program = TRUE;
11479   si->quick_switch = FALSE;
11480   si->input_on_focus = FALSE;
11481   si->prefer_aga_graphics = TRUE;
11482   si->prefer_lowpass_sounds = FALSE;
11483   si->prefer_extra_panel_items = TRUE;
11484   si->game_speed_extended = FALSE;
11485   si->game_frame_delay = GAME_FRAME_DELAY;
11486   si->bd_skip_uncovering = FALSE;
11487   si->bd_skip_hatching = FALSE;
11488   si->bd_scroll_delay = TRUE;
11489   si->bd_smooth_movements = AUTO;
11490   si->sp_show_border_elements = FALSE;
11491   si->small_game_graphics = FALSE;
11492   si->show_load_save_buttons = FALSE;
11493   si->show_undo_redo_buttons = FALSE;
11494   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11495
11496   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11497   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11498   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11499
11500   si->override_level_graphics = FALSE;
11501   si->override_level_sounds = FALSE;
11502   si->override_level_music = FALSE;
11503
11504   si->volume_simple = 100;              // percent
11505   si->volume_loops = 100;               // percent
11506   si->volume_music = 100;               // percent
11507
11508   si->network_mode = FALSE;
11509   si->network_player_nr = 0;            // first player
11510   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11511
11512   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11513   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11514   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11515   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11516   si->touch.draw_outlined = TRUE;
11517   si->touch.draw_pressed = TRUE;
11518
11519   for (i = 0; i < 2; i++)
11520   {
11521     char *default_grid_button[6][2] =
11522     {
11523       { "      ", "  ^^  " },
11524       { "      ", "  ^^  " },
11525       { "      ", "<<  >>" },
11526       { "      ", "<<  >>" },
11527       { "111222", "  vv  " },
11528       { "111222", "  vv  " }
11529     };
11530     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11531     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11532     int min_xsize = MIN(6, grid_xsize);
11533     int min_ysize = MIN(6, grid_ysize);
11534     int startx = grid_xsize - min_xsize;
11535     int starty = grid_ysize - min_ysize;
11536     int x, y;
11537
11538     // virtual buttons grid can only be set to defaults if video is initialized
11539     // (this will be repeated if virtual buttons are not loaded from setup file)
11540     if (video.initialized)
11541     {
11542       si->touch.grid_xsize[i] = grid_xsize;
11543       si->touch.grid_ysize[i] = grid_ysize;
11544     }
11545     else
11546     {
11547       si->touch.grid_xsize[i] = -1;
11548       si->touch.grid_ysize[i] = -1;
11549     }
11550
11551     for (x = 0; x < MAX_GRID_XSIZE; x++)
11552       for (y = 0; y < MAX_GRID_YSIZE; y++)
11553         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11554
11555     for (x = 0; x < min_xsize; x++)
11556       for (y = 0; y < min_ysize; y++)
11557         si->touch.grid_button[i][x][starty + y] =
11558           default_grid_button[y][0][x];
11559
11560     for (x = 0; x < min_xsize; x++)
11561       for (y = 0; y < min_ysize; y++)
11562         si->touch.grid_button[i][startx + x][starty + y] =
11563           default_grid_button[y][1][x];
11564   }
11565
11566   si->touch.grid_initialized            = video.initialized;
11567
11568   si->touch.overlay_buttons             = FALSE;
11569
11570   si->editor.el_boulderdash             = TRUE;
11571   si->editor.el_boulderdash_native      = TRUE;
11572   si->editor.el_boulderdash_effects     = TRUE;
11573   si->editor.el_emerald_mine            = TRUE;
11574   si->editor.el_emerald_mine_club       = TRUE;
11575   si->editor.el_more                    = TRUE;
11576   si->editor.el_sokoban                 = TRUE;
11577   si->editor.el_supaplex                = TRUE;
11578   si->editor.el_diamond_caves           = TRUE;
11579   si->editor.el_dx_boulderdash          = TRUE;
11580
11581   si->editor.el_mirror_magic            = TRUE;
11582   si->editor.el_deflektor               = TRUE;
11583
11584   si->editor.el_chars                   = TRUE;
11585   si->editor.el_steel_chars             = TRUE;
11586
11587   si->editor.el_classic                 = TRUE;
11588   si->editor.el_custom                  = TRUE;
11589
11590   si->editor.el_user_defined            = FALSE;
11591   si->editor.el_dynamic                 = TRUE;
11592
11593   si->editor.el_headlines               = TRUE;
11594
11595   si->editor.show_element_token         = FALSE;
11596
11597   si->editor.show_read_only_warning     = TRUE;
11598
11599   si->editor.use_template_for_new_levels = TRUE;
11600
11601   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11602   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11603   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11604   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11605   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11606
11607   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11608   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11609   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11610   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11611   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11612
11613   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11614   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11615   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11616   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11617   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11618   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11619
11620   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11621   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11622   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11623
11624   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11625   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11626   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11627   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11628
11629   for (i = 0; i < MAX_PLAYERS; i++)
11630   {
11631     si->input[i].use_joystick = FALSE;
11632     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11633     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11634     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11635     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11636     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11637     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11638     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11639     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11640     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11641     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11642     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11643     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11644     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11645     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11646     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11647   }
11648
11649   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11650   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11651   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11652   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11653
11654   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11655   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11656   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11657   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11658   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11659   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11660   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11661
11662   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11663
11664   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11665   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11666   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11667
11668   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11669   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11670   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11671
11672   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11673   si->internal.choose_from_top_leveldir = FALSE;
11674   si->internal.show_scaling_in_title = TRUE;
11675   si->internal.create_user_levelset = TRUE;
11676   si->internal.info_screens_from_main = FALSE;
11677
11678   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11679   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11680
11681   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11682   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11683   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11684   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11685   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11686   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11687   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11688   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11689   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11690   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11691
11692   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11693   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11694   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11695   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11696   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11697   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11698   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11699   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11700   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11701   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11702
11703   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11704   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11705
11706   si->debug.show_frames_per_second = FALSE;
11707
11708   si->debug.xsn_mode = AUTO;
11709   si->debug.xsn_percent = 0;
11710
11711   si->options.verbose = FALSE;
11712   si->options.debug = FALSE;
11713   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11714
11715 #if defined(PLATFORM_ANDROID)
11716   si->fullscreen = TRUE;
11717   si->touch.overlay_buttons = TRUE;
11718 #endif
11719
11720   setHideSetupEntry(&setup.debug.xsn_mode);
11721 }
11722
11723 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11724 {
11725   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11726 }
11727
11728 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11729 {
11730   si->player_uuid = NULL;       // (will be set later)
11731   si->player_version = 1;       // (will be set later)
11732
11733   si->use_api_server = TRUE;
11734   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11735   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11736   si->ask_for_uploading_tapes = TRUE;
11737   si->ask_for_remaining_tapes = FALSE;
11738   si->provide_uploading_tapes = TRUE;
11739   si->ask_for_using_api_server = TRUE;
11740   si->has_remaining_tapes = FALSE;
11741 }
11742
11743 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11744 {
11745   si->editor_cascade.el_bd              = TRUE;
11746   si->editor_cascade.el_bd_native       = TRUE;
11747   si->editor_cascade.el_bd_effects      = FALSE;
11748   si->editor_cascade.el_em              = TRUE;
11749   si->editor_cascade.el_emc             = TRUE;
11750   si->editor_cascade.el_rnd             = TRUE;
11751   si->editor_cascade.el_sb              = TRUE;
11752   si->editor_cascade.el_sp              = TRUE;
11753   si->editor_cascade.el_dc              = TRUE;
11754   si->editor_cascade.el_dx              = TRUE;
11755
11756   si->editor_cascade.el_mm              = TRUE;
11757   si->editor_cascade.el_df              = TRUE;
11758
11759   si->editor_cascade.el_chars           = FALSE;
11760   si->editor_cascade.el_steel_chars     = FALSE;
11761   si->editor_cascade.el_ce              = FALSE;
11762   si->editor_cascade.el_ge              = FALSE;
11763   si->editor_cascade.el_es              = FALSE;
11764   si->editor_cascade.el_ref             = FALSE;
11765   si->editor_cascade.el_user            = FALSE;
11766   si->editor_cascade.el_dynamic         = FALSE;
11767 }
11768
11769 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11770
11771 static char *getHideSetupToken(void *setup_value)
11772 {
11773   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11774
11775   if (setup_value != NULL)
11776     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11777
11778   return hide_setup_token;
11779 }
11780
11781 void setHideSetupEntry(void *setup_value)
11782 {
11783   char *hide_setup_token = getHideSetupToken(setup_value);
11784
11785   if (hide_setup_hash == NULL)
11786     hide_setup_hash = newSetupFileHash();
11787
11788   if (setup_value != NULL)
11789     setHashEntry(hide_setup_hash, hide_setup_token, "");
11790 }
11791
11792 void removeHideSetupEntry(void *setup_value)
11793 {
11794   char *hide_setup_token = getHideSetupToken(setup_value);
11795
11796   if (setup_value != NULL)
11797     removeHashEntry(hide_setup_hash, hide_setup_token);
11798 }
11799
11800 boolean hideSetupEntry(void *setup_value)
11801 {
11802   char *hide_setup_token = getHideSetupToken(setup_value);
11803
11804   return (setup_value != NULL &&
11805           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11806 }
11807
11808 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11809                                       struct TokenInfo *token_info,
11810                                       int token_nr, char *token_text)
11811 {
11812   char *token_hide_text = getStringCat2(token_text, ".hide");
11813   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11814
11815   // set the value of this setup option in the setup option structure
11816   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11817
11818   // check if this setup option should be hidden in the setup menu
11819   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11820     setHideSetupEntry(token_info[token_nr].value);
11821
11822   free(token_hide_text);
11823 }
11824
11825 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11826                                       struct TokenInfo *token_info,
11827                                       int token_nr)
11828 {
11829   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11830                             token_info[token_nr].text);
11831 }
11832
11833 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11834 {
11835   int i, pnr;
11836
11837   if (!setup_file_hash)
11838     return;
11839
11840   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11841     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11842
11843   setup.touch.grid_initialized = TRUE;
11844   for (i = 0; i < 2; i++)
11845   {
11846     int grid_xsize = setup.touch.grid_xsize[i];
11847     int grid_ysize = setup.touch.grid_ysize[i];
11848     int x, y;
11849
11850     // if virtual buttons are not loaded from setup file, repeat initializing
11851     // virtual buttons grid with default values later when video is initialized
11852     if (grid_xsize == -1 ||
11853         grid_ysize == -1)
11854     {
11855       setup.touch.grid_initialized = FALSE;
11856
11857       continue;
11858     }
11859
11860     for (y = 0; y < grid_ysize; y++)
11861     {
11862       char token_string[MAX_LINE_LEN];
11863
11864       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11865
11866       char *value_string = getHashEntry(setup_file_hash, token_string);
11867
11868       if (value_string == NULL)
11869         continue;
11870
11871       for (x = 0; x < grid_xsize; x++)
11872       {
11873         char c = value_string[x];
11874
11875         setup.touch.grid_button[i][x][y] =
11876           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11877       }
11878     }
11879   }
11880
11881   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11882     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11883
11884   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11885     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11886
11887   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11888   {
11889     char prefix[30];
11890
11891     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11892
11893     setup_input = setup.input[pnr];
11894     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11895     {
11896       char full_token[100];
11897
11898       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11899       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11900                                 full_token);
11901     }
11902     setup.input[pnr] = setup_input;
11903   }
11904
11905   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11906     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11907
11908   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11909     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11910
11911   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11912     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11913
11914   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11915     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11916
11917   setHideRelatedSetupEntries();
11918 }
11919
11920 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11921 {
11922   int i;
11923
11924   if (!setup_file_hash)
11925     return;
11926
11927   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11928     setSetupInfo(auto_setup_tokens, i,
11929                  getHashEntry(setup_file_hash,
11930                               auto_setup_tokens[i].text));
11931 }
11932
11933 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11934 {
11935   int i;
11936
11937   if (!setup_file_hash)
11938     return;
11939
11940   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11941     setSetupInfo(server_setup_tokens, i,
11942                  getHashEntry(setup_file_hash,
11943                               server_setup_tokens[i].text));
11944 }
11945
11946 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11947 {
11948   int i;
11949
11950   if (!setup_file_hash)
11951     return;
11952
11953   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11954     setSetupInfo(editor_cascade_setup_tokens, i,
11955                  getHashEntry(setup_file_hash,
11956                               editor_cascade_setup_tokens[i].text));
11957 }
11958
11959 void LoadUserNames(void)
11960 {
11961   int last_user_nr = user.nr;
11962   int i;
11963
11964   if (global.user_names != NULL)
11965   {
11966     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11967       checked_free(global.user_names[i]);
11968
11969     checked_free(global.user_names);
11970   }
11971
11972   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11973
11974   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11975   {
11976     user.nr = i;
11977
11978     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11979
11980     if (setup_file_hash)
11981     {
11982       char *player_name = getHashEntry(setup_file_hash, "player_name");
11983
11984       global.user_names[i] = getFixedUserName(player_name);
11985
11986       freeSetupFileHash(setup_file_hash);
11987     }
11988
11989     if (global.user_names[i] == NULL)
11990       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11991   }
11992
11993   user.nr = last_user_nr;
11994 }
11995
11996 void LoadSetupFromFilename(char *filename)
11997 {
11998   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11999
12000   if (setup_file_hash)
12001   {
12002     decodeSetupFileHash_Default(setup_file_hash);
12003
12004     freeSetupFileHash(setup_file_hash);
12005   }
12006   else
12007   {
12008     Debug("setup", "using default setup values");
12009   }
12010 }
12011
12012 static void LoadSetup_SpecialPostProcessing(void)
12013 {
12014   char *player_name_new;
12015
12016   // needed to work around problems with fixed length strings
12017   player_name_new = getFixedUserName(setup.player_name);
12018   free(setup.player_name);
12019   setup.player_name = player_name_new;
12020
12021   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12022   if (setup.scroll_delay == FALSE)
12023   {
12024     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12025     setup.scroll_delay = TRUE;                  // now always "on"
12026   }
12027
12028   // make sure that scroll delay value stays inside valid range
12029   setup.scroll_delay_value =
12030     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12031 }
12032
12033 void LoadSetup_Default(void)
12034 {
12035   char *filename;
12036
12037   // always start with reliable default values
12038   setSetupInfoToDefaults(&setup);
12039
12040   // try to load setup values from default setup file
12041   filename = getDefaultSetupFilename();
12042
12043   if (fileExists(filename))
12044     LoadSetupFromFilename(filename);
12045
12046   // try to load setup values from platform setup file
12047   filename = getPlatformSetupFilename();
12048
12049   if (fileExists(filename))
12050     LoadSetupFromFilename(filename);
12051
12052   // try to load setup values from user setup file
12053   filename = getSetupFilename();
12054
12055   LoadSetupFromFilename(filename);
12056
12057   LoadSetup_SpecialPostProcessing();
12058 }
12059
12060 void LoadSetup_AutoSetup(void)
12061 {
12062   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12063   SetupFileHash *setup_file_hash = NULL;
12064
12065   // always start with reliable default values
12066   setSetupInfoToDefaults_AutoSetup(&setup);
12067
12068   setup_file_hash = loadSetupFileHash(filename);
12069
12070   if (setup_file_hash)
12071   {
12072     decodeSetupFileHash_AutoSetup(setup_file_hash);
12073
12074     freeSetupFileHash(setup_file_hash);
12075   }
12076
12077   free(filename);
12078 }
12079
12080 void LoadSetup_ServerSetup(void)
12081 {
12082   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12083   SetupFileHash *setup_file_hash = NULL;
12084
12085   // always start with reliable default values
12086   setSetupInfoToDefaults_ServerSetup(&setup);
12087
12088   setup_file_hash = loadSetupFileHash(filename);
12089
12090   if (setup_file_hash)
12091   {
12092     decodeSetupFileHash_ServerSetup(setup_file_hash);
12093
12094     freeSetupFileHash(setup_file_hash);
12095   }
12096
12097   free(filename);
12098
12099   if (setup.player_uuid == NULL)
12100   {
12101     // player UUID does not yet exist in setup file
12102     setup.player_uuid = getStringCopy(getUUID());
12103     setup.player_version = 2;
12104
12105     SaveSetup_ServerSetup();
12106   }
12107 }
12108
12109 void LoadSetup_EditorCascade(void)
12110 {
12111   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12112   SetupFileHash *setup_file_hash = NULL;
12113
12114   // always start with reliable default values
12115   setSetupInfoToDefaults_EditorCascade(&setup);
12116
12117   setup_file_hash = loadSetupFileHash(filename);
12118
12119   if (setup_file_hash)
12120   {
12121     decodeSetupFileHash_EditorCascade(setup_file_hash);
12122
12123     freeSetupFileHash(setup_file_hash);
12124   }
12125
12126   free(filename);
12127 }
12128
12129 void LoadSetup(void)
12130 {
12131   LoadSetup_Default();
12132   LoadSetup_AutoSetup();
12133   LoadSetup_ServerSetup();
12134   LoadSetup_EditorCascade();
12135 }
12136
12137 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12138                                            char *mapping_line)
12139 {
12140   char mapping_guid[MAX_LINE_LEN];
12141   char *mapping_start, *mapping_end;
12142
12143   // get GUID from game controller mapping line: copy complete line
12144   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12145   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12146
12147   // get GUID from game controller mapping line: cut after GUID part
12148   mapping_start = strchr(mapping_guid, ',');
12149   if (mapping_start != NULL)
12150     *mapping_start = '\0';
12151
12152   // cut newline from game controller mapping line
12153   mapping_end = strchr(mapping_line, '\n');
12154   if (mapping_end != NULL)
12155     *mapping_end = '\0';
12156
12157   // add mapping entry to game controller mappings hash
12158   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12159 }
12160
12161 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12162                                                  char *filename)
12163 {
12164   FILE *file;
12165
12166   if (!(file = fopen(filename, MODE_READ)))
12167   {
12168     Warn("cannot read game controller mappings file '%s'", filename);
12169
12170     return;
12171   }
12172
12173   while (!feof(file))
12174   {
12175     char line[MAX_LINE_LEN];
12176
12177     if (!fgets(line, MAX_LINE_LEN, file))
12178       break;
12179
12180     addGameControllerMappingToHash(mappings_hash, line);
12181   }
12182
12183   fclose(file);
12184 }
12185
12186 void SaveSetup_Default(void)
12187 {
12188   char *filename = getSetupFilename();
12189   FILE *file;
12190   int i, pnr;
12191
12192   InitUserDataDirectory();
12193
12194   if (!(file = fopen(filename, MODE_WRITE)))
12195   {
12196     Warn("cannot write setup file '%s'", filename);
12197
12198     return;
12199   }
12200
12201   fprintFileHeader(file, SETUP_FILENAME);
12202
12203   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12204   {
12205     // just to make things nicer :)
12206     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12207         global_setup_tokens[i].value == &setup.sound                    ||
12208         global_setup_tokens[i].value == &setup.graphics_set             ||
12209         global_setup_tokens[i].value == &setup.volume_simple            ||
12210         global_setup_tokens[i].value == &setup.network_mode             ||
12211         global_setup_tokens[i].value == &setup.touch.control_type       ||
12212         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12213         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12214       fprintf(file, "\n");
12215
12216     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12217   }
12218
12219   for (i = 0; i < 2; i++)
12220   {
12221     int grid_xsize = setup.touch.grid_xsize[i];
12222     int grid_ysize = setup.touch.grid_ysize[i];
12223     int x, y;
12224
12225     fprintf(file, "\n");
12226
12227     for (y = 0; y < grid_ysize; y++)
12228     {
12229       char token_string[MAX_LINE_LEN];
12230       char value_string[MAX_LINE_LEN];
12231
12232       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12233
12234       for (x = 0; x < grid_xsize; x++)
12235       {
12236         char c = setup.touch.grid_button[i][x][y];
12237
12238         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12239       }
12240
12241       value_string[grid_xsize] = '\0';
12242
12243       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12244     }
12245   }
12246
12247   fprintf(file, "\n");
12248   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12249     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12250
12251   fprintf(file, "\n");
12252   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12253     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12254
12255   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12256   {
12257     char prefix[30];
12258
12259     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12260     fprintf(file, "\n");
12261
12262     setup_input = setup.input[pnr];
12263     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12264       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12265   }
12266
12267   fprintf(file, "\n");
12268   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12269     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12270
12271   // (internal setup values not saved to user setup file)
12272
12273   fprintf(file, "\n");
12274   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12275     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12276         setup.debug.xsn_mode != AUTO)
12277       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12278
12279   fprintf(file, "\n");
12280   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12281     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12282
12283   fclose(file);
12284
12285   SetFilePermissions(filename, PERMS_PRIVATE);
12286 }
12287
12288 void SaveSetup_AutoSetup(void)
12289 {
12290   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12291   FILE *file;
12292   int i;
12293
12294   InitUserDataDirectory();
12295
12296   if (!(file = fopen(filename, MODE_WRITE)))
12297   {
12298     Warn("cannot write auto setup file '%s'", filename);
12299
12300     free(filename);
12301
12302     return;
12303   }
12304
12305   fprintFileHeader(file, AUTOSETUP_FILENAME);
12306
12307   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12308     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12309
12310   fclose(file);
12311
12312   SetFilePermissions(filename, PERMS_PRIVATE);
12313
12314   free(filename);
12315 }
12316
12317 void SaveSetup_ServerSetup(void)
12318 {
12319   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12320   FILE *file;
12321   int i;
12322
12323   InitUserDataDirectory();
12324
12325   if (!(file = fopen(filename, MODE_WRITE)))
12326   {
12327     Warn("cannot write server setup file '%s'", filename);
12328
12329     free(filename);
12330
12331     return;
12332   }
12333
12334   fprintFileHeader(file, SERVERSETUP_FILENAME);
12335
12336   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12337   {
12338     // just to make things nicer :)
12339     if (server_setup_tokens[i].value == &setup.use_api_server)
12340       fprintf(file, "\n");
12341
12342     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12343   }
12344
12345   fclose(file);
12346
12347   SetFilePermissions(filename, PERMS_PRIVATE);
12348
12349   free(filename);
12350 }
12351
12352 void SaveSetup_EditorCascade(void)
12353 {
12354   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12355   FILE *file;
12356   int i;
12357
12358   InitUserDataDirectory();
12359
12360   if (!(file = fopen(filename, MODE_WRITE)))
12361   {
12362     Warn("cannot write editor cascade state file '%s'", filename);
12363
12364     free(filename);
12365
12366     return;
12367   }
12368
12369   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12370
12371   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12372     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12373
12374   fclose(file);
12375
12376   SetFilePermissions(filename, PERMS_PRIVATE);
12377
12378   free(filename);
12379 }
12380
12381 void SaveSetup(void)
12382 {
12383   SaveSetup_Default();
12384   SaveSetup_AutoSetup();
12385   SaveSetup_ServerSetup();
12386   SaveSetup_EditorCascade();
12387 }
12388
12389 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12390                                                   char *filename)
12391 {
12392   FILE *file;
12393
12394   if (!(file = fopen(filename, MODE_WRITE)))
12395   {
12396     Warn("cannot write game controller mappings file '%s'", filename);
12397
12398     return;
12399   }
12400
12401   BEGIN_HASH_ITERATION(mappings_hash, itr)
12402   {
12403     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12404   }
12405   END_HASH_ITERATION(mappings_hash, itr)
12406
12407   fclose(file);
12408 }
12409
12410 void SaveSetup_AddGameControllerMapping(char *mapping)
12411 {
12412   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12413   SetupFileHash *mappings_hash = newSetupFileHash();
12414
12415   InitUserDataDirectory();
12416
12417   // load existing personal game controller mappings
12418   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12419
12420   // add new mapping to personal game controller mappings
12421   addGameControllerMappingToHash(mappings_hash, mapping);
12422
12423   // save updated personal game controller mappings
12424   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12425
12426   freeSetupFileHash(mappings_hash);
12427   free(filename);
12428 }
12429
12430 void LoadCustomElementDescriptions(void)
12431 {
12432   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12433   SetupFileHash *setup_file_hash;
12434   int i;
12435
12436   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12437   {
12438     if (element_info[i].custom_description != NULL)
12439     {
12440       free(element_info[i].custom_description);
12441       element_info[i].custom_description = NULL;
12442     }
12443   }
12444
12445   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12446     return;
12447
12448   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12449   {
12450     char *token = getStringCat2(element_info[i].token_name, ".name");
12451     char *value = getHashEntry(setup_file_hash, token);
12452
12453     if (value != NULL)
12454       element_info[i].custom_description = getStringCopy(value);
12455
12456     free(token);
12457   }
12458
12459   freeSetupFileHash(setup_file_hash);
12460 }
12461
12462 static int getElementFromToken(char *token)
12463 {
12464   char *value = getHashEntry(element_token_hash, token);
12465
12466   if (value != NULL)
12467     return atoi(value);
12468
12469   Warn("unknown element token '%s'", token);
12470
12471   return EL_UNDEFINED;
12472 }
12473
12474 void FreeGlobalAnimEventInfo(void)
12475 {
12476   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12477
12478   if (gaei->event_list == NULL)
12479     return;
12480
12481   int i;
12482
12483   for (i = 0; i < gaei->num_event_lists; i++)
12484   {
12485     checked_free(gaei->event_list[i]->event_value);
12486     checked_free(gaei->event_list[i]);
12487   }
12488
12489   checked_free(gaei->event_list);
12490
12491   gaei->event_list = NULL;
12492   gaei->num_event_lists = 0;
12493 }
12494
12495 static int AddGlobalAnimEventList(void)
12496 {
12497   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12498   int list_pos = gaei->num_event_lists++;
12499
12500   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12501                                      sizeof(struct GlobalAnimEventListInfo *));
12502
12503   gaei->event_list[list_pos] =
12504     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12505
12506   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12507
12508   gaeli->event_value = NULL;
12509   gaeli->num_event_values = 0;
12510
12511   return list_pos;
12512 }
12513
12514 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12515 {
12516   // do not add empty global animation events
12517   if (event_value == ANIM_EVENT_NONE)
12518     return list_pos;
12519
12520   // if list position is undefined, create new list
12521   if (list_pos == ANIM_EVENT_UNDEFINED)
12522     list_pos = AddGlobalAnimEventList();
12523
12524   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12525   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12526   int value_pos = gaeli->num_event_values++;
12527
12528   gaeli->event_value = checked_realloc(gaeli->event_value,
12529                                        gaeli->num_event_values * sizeof(int *));
12530
12531   gaeli->event_value[value_pos] = event_value;
12532
12533   return list_pos;
12534 }
12535
12536 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12537 {
12538   if (list_pos == ANIM_EVENT_UNDEFINED)
12539     return ANIM_EVENT_NONE;
12540
12541   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12542   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12543
12544   return gaeli->event_value[value_pos];
12545 }
12546
12547 int GetGlobalAnimEventValueCount(int list_pos)
12548 {
12549   if (list_pos == ANIM_EVENT_UNDEFINED)
12550     return 0;
12551
12552   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12553   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12554
12555   return gaeli->num_event_values;
12556 }
12557
12558 // This function checks if a string <s> of the format "string1, string2, ..."
12559 // exactly contains a string <s_contained>.
12560
12561 static boolean string_has_parameter(char *s, char *s_contained)
12562 {
12563   char *substring;
12564
12565   if (s == NULL || s_contained == NULL)
12566     return FALSE;
12567
12568   if (strlen(s_contained) > strlen(s))
12569     return FALSE;
12570
12571   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12572   {
12573     char next_char = s[strlen(s_contained)];
12574
12575     // check if next character is delimiter or whitespace
12576     if (next_char == ',' || next_char == '\0' ||
12577         next_char == ' ' || next_char == '\t')
12578       return TRUE;
12579   }
12580
12581   // check if string contains another parameter string after a comma
12582   substring = strchr(s, ',');
12583   if (substring == NULL)        // string does not contain a comma
12584     return FALSE;
12585
12586   // advance string pointer to next character after the comma
12587   substring++;
12588
12589   // skip potential whitespaces after the comma
12590   while (*substring == ' ' || *substring == '\t')
12591     substring++;
12592
12593   return string_has_parameter(substring, s_contained);
12594 }
12595
12596 static int get_anim_parameter_value_ce(char *s)
12597 {
12598   char *s_ptr = s;
12599   char *pattern_1 = "ce_change:custom_";
12600   char *pattern_2 = ".page_";
12601   int pattern_1_len = strlen(pattern_1);
12602   char *matching_char = strstr(s_ptr, pattern_1);
12603   int result = ANIM_EVENT_NONE;
12604
12605   if (matching_char == NULL)
12606     return ANIM_EVENT_NONE;
12607
12608   result = ANIM_EVENT_CE_CHANGE;
12609
12610   s_ptr = matching_char + pattern_1_len;
12611
12612   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12613   if (*s_ptr >= '0' && *s_ptr <= '9')
12614   {
12615     int gic_ce_nr = (*s_ptr++ - '0');
12616
12617     if (*s_ptr >= '0' && *s_ptr <= '9')
12618     {
12619       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12620
12621       if (*s_ptr >= '0' && *s_ptr <= '9')
12622         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12623     }
12624
12625     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12626       return ANIM_EVENT_NONE;
12627
12628     // custom element stored as 0 to 255
12629     gic_ce_nr--;
12630
12631     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12632   }
12633   else
12634   {
12635     // invalid custom element number specified
12636
12637     return ANIM_EVENT_NONE;
12638   }
12639
12640   // check for change page number ("page_X" or "page_XX") (optional)
12641   if (strPrefix(s_ptr, pattern_2))
12642   {
12643     s_ptr += strlen(pattern_2);
12644
12645     if (*s_ptr >= '0' && *s_ptr <= '9')
12646     {
12647       int gic_page_nr = (*s_ptr++ - '0');
12648
12649       if (*s_ptr >= '0' && *s_ptr <= '9')
12650         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12651
12652       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12653         return ANIM_EVENT_NONE;
12654
12655       // change page stored as 1 to 32 (0 means "all change pages")
12656
12657       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12658     }
12659     else
12660     {
12661       // invalid animation part number specified
12662
12663       return ANIM_EVENT_NONE;
12664     }
12665   }
12666
12667   // discard result if next character is neither delimiter nor whitespace
12668   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12669         *s_ptr == ' ' || *s_ptr == '\t'))
12670     return ANIM_EVENT_NONE;
12671
12672   return result;
12673 }
12674
12675 static int get_anim_parameter_value(char *s)
12676 {
12677   int event_value[] =
12678   {
12679     ANIM_EVENT_CLICK,
12680     ANIM_EVENT_INIT,
12681     ANIM_EVENT_START,
12682     ANIM_EVENT_END,
12683     ANIM_EVENT_POST
12684   };
12685   char *pattern_1[] =
12686   {
12687     "click:anim_",
12688     "init:anim_",
12689     "start:anim_",
12690     "end:anim_",
12691     "post:anim_"
12692   };
12693   char *pattern_2 = ".part_";
12694   char *matching_char = NULL;
12695   char *s_ptr = s;
12696   int pattern_1_len = 0;
12697   int result = ANIM_EVENT_NONE;
12698   int i;
12699
12700   result = get_anim_parameter_value_ce(s);
12701
12702   if (result != ANIM_EVENT_NONE)
12703     return result;
12704
12705   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12706   {
12707     matching_char = strstr(s_ptr, pattern_1[i]);
12708     pattern_1_len = strlen(pattern_1[i]);
12709     result = event_value[i];
12710
12711     if (matching_char != NULL)
12712       break;
12713   }
12714
12715   if (matching_char == NULL)
12716     return ANIM_EVENT_NONE;
12717
12718   s_ptr = matching_char + pattern_1_len;
12719
12720   // check for main animation number ("anim_X" or "anim_XX")
12721   if (*s_ptr >= '0' && *s_ptr <= '9')
12722   {
12723     int gic_anim_nr = (*s_ptr++ - '0');
12724
12725     if (*s_ptr >= '0' && *s_ptr <= '9')
12726       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12727
12728     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12729       return ANIM_EVENT_NONE;
12730
12731     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12732   }
12733   else
12734   {
12735     // invalid main animation number specified
12736
12737     return ANIM_EVENT_NONE;
12738   }
12739
12740   // check for animation part number ("part_X" or "part_XX") (optional)
12741   if (strPrefix(s_ptr, pattern_2))
12742   {
12743     s_ptr += strlen(pattern_2);
12744
12745     if (*s_ptr >= '0' && *s_ptr <= '9')
12746     {
12747       int gic_part_nr = (*s_ptr++ - '0');
12748
12749       if (*s_ptr >= '0' && *s_ptr <= '9')
12750         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12751
12752       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12753         return ANIM_EVENT_NONE;
12754
12755       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12756     }
12757     else
12758     {
12759       // invalid animation part number specified
12760
12761       return ANIM_EVENT_NONE;
12762     }
12763   }
12764
12765   // discard result if next character is neither delimiter nor whitespace
12766   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12767         *s_ptr == ' ' || *s_ptr == '\t'))
12768     return ANIM_EVENT_NONE;
12769
12770   return result;
12771 }
12772
12773 static int get_anim_parameter_values(char *s)
12774 {
12775   int list_pos = ANIM_EVENT_UNDEFINED;
12776   int event_value = ANIM_EVENT_DEFAULT;
12777
12778   if (string_has_parameter(s, "any"))
12779     event_value |= ANIM_EVENT_ANY;
12780
12781   if (string_has_parameter(s, "click:self") ||
12782       string_has_parameter(s, "click") ||
12783       string_has_parameter(s, "self"))
12784     event_value |= ANIM_EVENT_SELF;
12785
12786   if (string_has_parameter(s, "unclick:any"))
12787     event_value |= ANIM_EVENT_UNCLICK_ANY;
12788
12789   // if animation event found, add it to global animation event list
12790   if (event_value != ANIM_EVENT_NONE)
12791     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12792
12793   while (s != NULL)
12794   {
12795     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12796     event_value = get_anim_parameter_value(s);
12797
12798     // if animation event found, add it to global animation event list
12799     if (event_value != ANIM_EVENT_NONE)
12800       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12801
12802     // continue with next part of the string, starting with next comma
12803     s = strchr(s + 1, ',');
12804   }
12805
12806   return list_pos;
12807 }
12808
12809 static int get_anim_action_parameter_value(char *token)
12810 {
12811   // check most common default case first to massively speed things up
12812   if (strEqual(token, ARG_UNDEFINED))
12813     return ANIM_EVENT_ACTION_NONE;
12814
12815   int result = getImageIDFromToken(token);
12816
12817   if (result == -1)
12818   {
12819     char *gfx_token = getStringCat2("gfx.", token);
12820
12821     result = getImageIDFromToken(gfx_token);
12822
12823     checked_free(gfx_token);
12824   }
12825
12826   if (result == -1)
12827   {
12828     Key key = getKeyFromX11KeyName(token);
12829
12830     if (key != KSYM_UNDEFINED)
12831       result = -(int)key;
12832   }
12833
12834   if (result == -1)
12835   {
12836     if (isURL(token))
12837     {
12838       result = get_hash_from_string(token);     // unsigned int => int
12839       result = ABS(result);                     // may be negative now
12840       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12841
12842       setHashEntry(anim_url_hash, int2str(result, 0), token);
12843     }
12844   }
12845
12846   if (result == -1)
12847     result = ANIM_EVENT_ACTION_NONE;
12848
12849   return result;
12850 }
12851
12852 int get_parameter_value(char *value_raw, char *suffix, int type)
12853 {
12854   char *value = getStringToLower(value_raw);
12855   int result = 0;       // probably a save default value
12856
12857   if (strEqual(suffix, ".direction"))
12858   {
12859     result = (strEqual(value, "left")  ? MV_LEFT :
12860               strEqual(value, "right") ? MV_RIGHT :
12861               strEqual(value, "up")    ? MV_UP :
12862               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12863   }
12864   else if (strEqual(suffix, ".position"))
12865   {
12866     result = (strEqual(value, "left")   ? POS_LEFT :
12867               strEqual(value, "right")  ? POS_RIGHT :
12868               strEqual(value, "top")    ? POS_TOP :
12869               strEqual(value, "upper")  ? POS_UPPER :
12870               strEqual(value, "middle") ? POS_MIDDLE :
12871               strEqual(value, "lower")  ? POS_LOWER :
12872               strEqual(value, "bottom") ? POS_BOTTOM :
12873               strEqual(value, "any")    ? POS_ANY :
12874               strEqual(value, "ce")     ? POS_CE :
12875               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12876               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12877   }
12878   else if (strEqual(suffix, ".align"))
12879   {
12880     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12881               strEqual(value, "right")  ? ALIGN_RIGHT :
12882               strEqual(value, "center") ? ALIGN_CENTER :
12883               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12884   }
12885   else if (strEqual(suffix, ".valign"))
12886   {
12887     result = (strEqual(value, "top")    ? VALIGN_TOP :
12888               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12889               strEqual(value, "middle") ? VALIGN_MIDDLE :
12890               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12891   }
12892   else if (strEqual(suffix, ".anim_mode"))
12893   {
12894     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12895               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12896               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12897               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12898               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12899               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12900               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12901               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12902               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12903               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12904               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12905               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12906               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12907               string_has_parameter(value, "all")        ? ANIM_ALL :
12908               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12909               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12910               ANIM_DEFAULT);
12911
12912     if (string_has_parameter(value, "once"))
12913       result |= ANIM_ONCE;
12914
12915     if (string_has_parameter(value, "reverse"))
12916       result |= ANIM_REVERSE;
12917
12918     if (string_has_parameter(value, "opaque_player"))
12919       result |= ANIM_OPAQUE_PLAYER;
12920
12921     if (string_has_parameter(value, "static_panel"))
12922       result |= ANIM_STATIC_PANEL;
12923   }
12924   else if (strEqual(suffix, ".init_event") ||
12925            strEqual(suffix, ".anim_event"))
12926   {
12927     result = get_anim_parameter_values(value);
12928   }
12929   else if (strEqual(suffix, ".init_delay_action") ||
12930            strEqual(suffix, ".anim_delay_action") ||
12931            strEqual(suffix, ".post_delay_action") ||
12932            strEqual(suffix, ".init_event_action") ||
12933            strEqual(suffix, ".anim_event_action"))
12934   {
12935     result = get_anim_action_parameter_value(value_raw);
12936   }
12937   else if (strEqual(suffix, ".class"))
12938   {
12939     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12940               get_hash_from_string(value));
12941   }
12942   else if (strEqual(suffix, ".style"))
12943   {
12944     result = STYLE_DEFAULT;
12945
12946     if (string_has_parameter(value, "accurate_borders"))
12947       result |= STYLE_ACCURATE_BORDERS;
12948
12949     if (string_has_parameter(value, "inner_corners"))
12950       result |= STYLE_INNER_CORNERS;
12951
12952     if (string_has_parameter(value, "reverse"))
12953       result |= STYLE_REVERSE;
12954
12955     if (string_has_parameter(value, "leftmost_position"))
12956       result |= STYLE_LEFTMOST_POSITION;
12957
12958     if (string_has_parameter(value, "block_clicks"))
12959       result |= STYLE_BLOCK;
12960
12961     if (string_has_parameter(value, "passthrough_clicks"))
12962       result |= STYLE_PASSTHROUGH;
12963
12964     if (string_has_parameter(value, "multiple_actions"))
12965       result |= STYLE_MULTIPLE_ACTIONS;
12966
12967     if (string_has_parameter(value, "consume_ce_event"))
12968       result |= STYLE_CONSUME_CE_EVENT;
12969   }
12970   else if (strEqual(suffix, ".fade_mode"))
12971   {
12972     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12973               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12974               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12975               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12976               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12977               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12978               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12979               FADE_MODE_DEFAULT);
12980   }
12981   else if (strEqual(suffix, ".auto_delay_unit"))
12982   {
12983     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12984               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12985               AUTO_DELAY_UNIT_DEFAULT);
12986   }
12987   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12988   {
12989     result = gfx.get_font_from_token_function(value);
12990   }
12991   else          // generic parameter of type integer or boolean
12992   {
12993     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12994               type == TYPE_INTEGER ? get_integer_from_string(value) :
12995               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12996               ARG_UNDEFINED_VALUE);
12997   }
12998
12999   free(value);
13000
13001   return result;
13002 }
13003
13004 static int get_token_parameter_value(char *token, char *value_raw)
13005 {
13006   char *suffix;
13007
13008   if (token == NULL || value_raw == NULL)
13009     return ARG_UNDEFINED_VALUE;
13010
13011   suffix = strrchr(token, '.');
13012   if (suffix == NULL)
13013     suffix = token;
13014
13015   if (strEqual(suffix, ".element"))
13016     return getElementFromToken(value_raw);
13017
13018   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13019   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13020 }
13021
13022 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13023                                      boolean ignore_defaults)
13024 {
13025   int i;
13026
13027   for (i = 0; image_config_vars[i].token != NULL; i++)
13028   {
13029     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13030
13031     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13032     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13033       continue;
13034
13035     if (value != NULL)
13036       *image_config_vars[i].value =
13037         get_token_parameter_value(image_config_vars[i].token, value);
13038   }
13039 }
13040
13041 void InitMenuDesignSettings_Static(void)
13042 {
13043   // always start with reliable default values from static default config
13044   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13045 }
13046
13047 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13048 {
13049   int i;
13050
13051   // the following initializes hierarchical values from static configuration
13052
13053   // special case: initialize "ARG_DEFAULT" values in static default config
13054   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13055   titlescreen_initial_first_default.fade_mode  =
13056     title_initial_first_default.fade_mode;
13057   titlescreen_initial_first_default.fade_delay =
13058     title_initial_first_default.fade_delay;
13059   titlescreen_initial_first_default.post_delay =
13060     title_initial_first_default.post_delay;
13061   titlescreen_initial_first_default.auto_delay =
13062     title_initial_first_default.auto_delay;
13063   titlescreen_initial_first_default.auto_delay_unit =
13064     title_initial_first_default.auto_delay_unit;
13065   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13066   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13067   titlescreen_first_default.post_delay = title_first_default.post_delay;
13068   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13069   titlescreen_first_default.auto_delay_unit =
13070     title_first_default.auto_delay_unit;
13071   titlemessage_initial_first_default.fade_mode  =
13072     title_initial_first_default.fade_mode;
13073   titlemessage_initial_first_default.fade_delay =
13074     title_initial_first_default.fade_delay;
13075   titlemessage_initial_first_default.post_delay =
13076     title_initial_first_default.post_delay;
13077   titlemessage_initial_first_default.auto_delay =
13078     title_initial_first_default.auto_delay;
13079   titlemessage_initial_first_default.auto_delay_unit =
13080     title_initial_first_default.auto_delay_unit;
13081   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13082   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13083   titlemessage_first_default.post_delay = title_first_default.post_delay;
13084   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13085   titlemessage_first_default.auto_delay_unit =
13086     title_first_default.auto_delay_unit;
13087
13088   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13089   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13090   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13091   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13092   titlescreen_initial_default.auto_delay_unit =
13093     title_initial_default.auto_delay_unit;
13094   titlescreen_default.fade_mode  = title_default.fade_mode;
13095   titlescreen_default.fade_delay = title_default.fade_delay;
13096   titlescreen_default.post_delay = title_default.post_delay;
13097   titlescreen_default.auto_delay = title_default.auto_delay;
13098   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13099   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13100   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13101   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13102   titlemessage_initial_default.auto_delay_unit =
13103     title_initial_default.auto_delay_unit;
13104   titlemessage_default.fade_mode  = title_default.fade_mode;
13105   titlemessage_default.fade_delay = title_default.fade_delay;
13106   titlemessage_default.post_delay = title_default.post_delay;
13107   titlemessage_default.auto_delay = title_default.auto_delay;
13108   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13109
13110   // special case: initialize "ARG_DEFAULT" values in static default config
13111   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13112   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13113   {
13114     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13115     titlescreen_first[i] = titlescreen_first_default;
13116     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13117     titlemessage_first[i] = titlemessage_first_default;
13118
13119     titlescreen_initial[i] = titlescreen_initial_default;
13120     titlescreen[i] = titlescreen_default;
13121     titlemessage_initial[i] = titlemessage_initial_default;
13122     titlemessage[i] = titlemessage_default;
13123   }
13124
13125   // special case: initialize "ARG_DEFAULT" values in static default config
13126   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13127   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13128   {
13129     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13130       continue;
13131
13132     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13133     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13134     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13135   }
13136
13137   // special case: initialize "ARG_DEFAULT" values in static default config
13138   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13139   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13140   {
13141     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13142     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13143     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13144
13145     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13146       continue;
13147
13148     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13149   }
13150 }
13151
13152 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13153 {
13154   static struct
13155   {
13156     struct XY *dst, *src;
13157   }
13158   game_buttons_xy[] =
13159   {
13160     { &game.button.save,        &game.button.stop       },
13161     { &game.button.pause2,      &game.button.pause      },
13162     { &game.button.load,        &game.button.play       },
13163     { &game.button.undo,        &game.button.stop       },
13164     { &game.button.redo,        &game.button.play       },
13165
13166     { NULL,                     NULL                    }
13167   };
13168   int i, j;
13169
13170   // special case: initialize later added SETUP list size from LEVELS value
13171   if (menu.list_size[GAME_MODE_SETUP] == -1)
13172     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13173
13174   // set default position for snapshot buttons to stop/pause/play buttons
13175   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13176     if ((*game_buttons_xy[i].dst).x == -1 &&
13177         (*game_buttons_xy[i].dst).y == -1)
13178       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13179
13180   // --------------------------------------------------------------------------
13181   // dynamic viewports (including playfield margins, borders and alignments)
13182   // --------------------------------------------------------------------------
13183
13184   // dynamic viewports currently only supported for landscape mode
13185   int display_width  = MAX(video.display_width, video.display_height);
13186   int display_height = MIN(video.display_width, video.display_height);
13187
13188   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13189   {
13190     struct RectWithBorder *vp_window    = &viewport.window[i];
13191     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13192     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13193     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13194     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13195     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13196     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13197     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13198
13199     // adjust window size if min/max width/height is specified
13200
13201     if (vp_window->min_width != -1)
13202     {
13203       int window_width = display_width;
13204
13205       // when using static window height, use aspect ratio of display
13206       if (vp_window->min_height == -1)
13207         window_width = vp_window->height * display_width / display_height;
13208
13209       vp_window->width = MAX(vp_window->min_width, window_width);
13210     }
13211
13212     if (vp_window->min_height != -1)
13213     {
13214       int window_height = display_height;
13215
13216       // when using static window width, use aspect ratio of display
13217       if (vp_window->min_width == -1)
13218         window_height = vp_window->width * display_height / display_width;
13219
13220       vp_window->height = MAX(vp_window->min_height, window_height);
13221     }
13222
13223     if (vp_window->max_width != -1)
13224       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13225
13226     if (vp_window->max_height != -1)
13227       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13228
13229     int playfield_width  = vp_window->width;
13230     int playfield_height = vp_window->height;
13231
13232     // adjust playfield size and position according to specified margins
13233
13234     playfield_width  -= vp_playfield->margin_left;
13235     playfield_width  -= vp_playfield->margin_right;
13236
13237     playfield_height -= vp_playfield->margin_top;
13238     playfield_height -= vp_playfield->margin_bottom;
13239
13240     // adjust playfield size if min/max width/height is specified
13241
13242     if (vp_playfield->min_width != -1)
13243       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13244
13245     if (vp_playfield->min_height != -1)
13246       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13247
13248     if (vp_playfield->max_width != -1)
13249       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13250
13251     if (vp_playfield->max_height != -1)
13252       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13253
13254     // adjust playfield position according to specified alignment
13255
13256     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13257       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13258     else if (vp_playfield->align == ALIGN_CENTER)
13259       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13260     else if (vp_playfield->align == ALIGN_RIGHT)
13261       vp_playfield->x += playfield_width - vp_playfield->width;
13262
13263     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13264       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13265     else if (vp_playfield->valign == VALIGN_MIDDLE)
13266       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13267     else if (vp_playfield->valign == VALIGN_BOTTOM)
13268       vp_playfield->y += playfield_height - vp_playfield->height;
13269
13270     vp_playfield->x += vp_playfield->margin_left;
13271     vp_playfield->y += vp_playfield->margin_top;
13272
13273     // adjust individual playfield borders if only default border is specified
13274
13275     if (vp_playfield->border_left == -1)
13276       vp_playfield->border_left = vp_playfield->border_size;
13277     if (vp_playfield->border_right == -1)
13278       vp_playfield->border_right = vp_playfield->border_size;
13279     if (vp_playfield->border_top == -1)
13280       vp_playfield->border_top = vp_playfield->border_size;
13281     if (vp_playfield->border_bottom == -1)
13282       vp_playfield->border_bottom = vp_playfield->border_size;
13283
13284     // set dynamic playfield borders if borders are specified as undefined
13285     // (but only if window size was dynamic and playfield size was static)
13286
13287     if (dynamic_window_width && !dynamic_playfield_width)
13288     {
13289       if (vp_playfield->border_left == -1)
13290       {
13291         vp_playfield->border_left = (vp_playfield->x -
13292                                      vp_playfield->margin_left);
13293         vp_playfield->x     -= vp_playfield->border_left;
13294         vp_playfield->width += vp_playfield->border_left;
13295       }
13296
13297       if (vp_playfield->border_right == -1)
13298       {
13299         vp_playfield->border_right = (vp_window->width -
13300                                       vp_playfield->x -
13301                                       vp_playfield->width -
13302                                       vp_playfield->margin_right);
13303         vp_playfield->width += vp_playfield->border_right;
13304       }
13305     }
13306
13307     if (dynamic_window_height && !dynamic_playfield_height)
13308     {
13309       if (vp_playfield->border_top == -1)
13310       {
13311         vp_playfield->border_top = (vp_playfield->y -
13312                                     vp_playfield->margin_top);
13313         vp_playfield->y      -= vp_playfield->border_top;
13314         vp_playfield->height += vp_playfield->border_top;
13315       }
13316
13317       if (vp_playfield->border_bottom == -1)
13318       {
13319         vp_playfield->border_bottom = (vp_window->height -
13320                                        vp_playfield->y -
13321                                        vp_playfield->height -
13322                                        vp_playfield->margin_bottom);
13323         vp_playfield->height += vp_playfield->border_bottom;
13324       }
13325     }
13326
13327     // adjust playfield size to be a multiple of a defined alignment tile size
13328
13329     int align_size = vp_playfield->align_size;
13330     int playfield_xtiles = vp_playfield->width  / align_size;
13331     int playfield_ytiles = vp_playfield->height / align_size;
13332     int playfield_width_corrected  = playfield_xtiles * align_size;
13333     int playfield_height_corrected = playfield_ytiles * align_size;
13334     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13335                                  i == GFX_SPECIAL_ARG_EDITOR);
13336
13337     if (is_playfield_mode &&
13338         dynamic_playfield_width &&
13339         vp_playfield->width != playfield_width_corrected)
13340     {
13341       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13342
13343       vp_playfield->width = playfield_width_corrected;
13344
13345       if (vp_playfield->align == ALIGN_LEFT)
13346       {
13347         vp_playfield->border_left += playfield_xdiff;
13348       }
13349       else if (vp_playfield->align == ALIGN_RIGHT)
13350       {
13351         vp_playfield->border_right += playfield_xdiff;
13352       }
13353       else if (vp_playfield->align == ALIGN_CENTER)
13354       {
13355         int border_left_diff  = playfield_xdiff / 2;
13356         int border_right_diff = playfield_xdiff - border_left_diff;
13357
13358         vp_playfield->border_left  += border_left_diff;
13359         vp_playfield->border_right += border_right_diff;
13360       }
13361     }
13362
13363     if (is_playfield_mode &&
13364         dynamic_playfield_height &&
13365         vp_playfield->height != playfield_height_corrected)
13366     {
13367       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13368
13369       vp_playfield->height = playfield_height_corrected;
13370
13371       if (vp_playfield->valign == VALIGN_TOP)
13372       {
13373         vp_playfield->border_top += playfield_ydiff;
13374       }
13375       else if (vp_playfield->align == VALIGN_BOTTOM)
13376       {
13377         vp_playfield->border_right += playfield_ydiff;
13378       }
13379       else if (vp_playfield->align == VALIGN_MIDDLE)
13380       {
13381         int border_top_diff    = playfield_ydiff / 2;
13382         int border_bottom_diff = playfield_ydiff - border_top_diff;
13383
13384         vp_playfield->border_top    += border_top_diff;
13385         vp_playfield->border_bottom += border_bottom_diff;
13386       }
13387     }
13388
13389     // adjust door positions according to specified alignment
13390
13391     for (j = 0; j < 2; j++)
13392     {
13393       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13394
13395       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13396         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13397       else if (vp_door->align == ALIGN_CENTER)
13398         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13399       else if (vp_door->align == ALIGN_RIGHT)
13400         vp_door->x += vp_window->width - vp_door->width;
13401
13402       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13403         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13404       else if (vp_door->valign == VALIGN_MIDDLE)
13405         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13406       else if (vp_door->valign == VALIGN_BOTTOM)
13407         vp_door->y += vp_window->height - vp_door->height;
13408     }
13409   }
13410 }
13411
13412 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13413 {
13414   static struct
13415   {
13416     struct XYTileSize *dst, *src;
13417     int graphic;
13418   }
13419   editor_buttons_xy[] =
13420   {
13421     {
13422       &editor.button.element_left,      &editor.palette.element_left,
13423       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13424     },
13425     {
13426       &editor.button.element_middle,    &editor.palette.element_middle,
13427       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13428     },
13429     {
13430       &editor.button.element_right,     &editor.palette.element_right,
13431       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13432     },
13433
13434     { NULL,                     NULL                    }
13435   };
13436   int i;
13437
13438   // set default position for element buttons to element graphics
13439   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13440   {
13441     if ((*editor_buttons_xy[i].dst).x == -1 &&
13442         (*editor_buttons_xy[i].dst).y == -1)
13443     {
13444       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13445
13446       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13447
13448       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13449     }
13450   }
13451
13452   // adjust editor palette rows and columns if specified to be dynamic
13453
13454   if (editor.palette.cols == -1)
13455   {
13456     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13457     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13458     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13459
13460     editor.palette.cols = (vp_width - sc_width) / bt_width;
13461
13462     if (editor.palette.x == -1)
13463     {
13464       int palette_width = editor.palette.cols * bt_width + sc_width;
13465
13466       editor.palette.x = (vp_width - palette_width) / 2;
13467     }
13468   }
13469
13470   if (editor.palette.rows == -1)
13471   {
13472     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13473     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13474     int tx_height = getFontHeight(FONT_TEXT_2);
13475
13476     editor.palette.rows = (vp_height - tx_height) / bt_height;
13477
13478     if (editor.palette.y == -1)
13479     {
13480       int palette_height = editor.palette.rows * bt_height + tx_height;
13481
13482       editor.palette.y = (vp_height - palette_height) / 2;
13483     }
13484   }
13485 }
13486
13487 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13488                                                       boolean initialize)
13489 {
13490   // special case: check if network and preview player positions are redefined,
13491   // to compare this later against the main menu level preview being redefined
13492   struct TokenIntPtrInfo menu_config_players[] =
13493   {
13494     { "main.network_players.x", &menu.main.network_players.redefined    },
13495     { "main.network_players.y", &menu.main.network_players.redefined    },
13496     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13497     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13498     { "preview.x",              &preview.redefined                      },
13499     { "preview.y",              &preview.redefined                      }
13500   };
13501   int i;
13502
13503   if (initialize)
13504   {
13505     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13506       *menu_config_players[i].value = FALSE;
13507   }
13508   else
13509   {
13510     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13511       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13512         *menu_config_players[i].value = TRUE;
13513   }
13514 }
13515
13516 static void InitMenuDesignSettings_PreviewPlayers(void)
13517 {
13518   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13519 }
13520
13521 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13522 {
13523   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13524 }
13525
13526 static void LoadMenuDesignSettingsFromFilename(char *filename)
13527 {
13528   static struct TitleFadingInfo tfi;
13529   static struct TitleMessageInfo tmi;
13530   static struct TokenInfo title_tokens[] =
13531   {
13532     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13533     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13534     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13535     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13536     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13537
13538     { -1,               NULL,                   NULL                    }
13539   };
13540   static struct TokenInfo titlemessage_tokens[] =
13541   {
13542     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13543     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13544     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13545     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13546     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13547     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13548     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13549     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13550     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13551     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13552     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13553     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13554     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13555     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13556     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13557     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13558     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13559     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13560
13561     { -1,               NULL,                   NULL                    }
13562   };
13563   static struct
13564   {
13565     struct TitleFadingInfo *info;
13566     char *text;
13567   }
13568   title_info[] =
13569   {
13570     // initialize first titles from "enter screen" definitions, if defined
13571     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13572     { &title_first_default,             "menu.enter_screen.TITLE"       },
13573
13574     // initialize title screens from "next screen" definitions, if defined
13575     { &title_initial_default,           "menu.next_screen.TITLE"        },
13576     { &title_default,                   "menu.next_screen.TITLE"        },
13577
13578     { NULL,                             NULL                            }
13579   };
13580   static struct
13581   {
13582     struct TitleMessageInfo *array;
13583     char *text;
13584   }
13585   titlemessage_arrays[] =
13586   {
13587     // initialize first titles from "enter screen" definitions, if defined
13588     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13589     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13590     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13591     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13592
13593     // initialize titles from "next screen" definitions, if defined
13594     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13595     { titlescreen,                      "menu.next_screen.TITLE"        },
13596     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13597     { titlemessage,                     "menu.next_screen.TITLE"        },
13598
13599     // overwrite titles with title definitions, if defined
13600     { titlescreen_initial_first,        "[title_initial]"               },
13601     { titlescreen_first,                "[title]"                       },
13602     { titlemessage_initial_first,       "[title_initial]"               },
13603     { titlemessage_first,               "[title]"                       },
13604
13605     { titlescreen_initial,              "[title_initial]"               },
13606     { titlescreen,                      "[title]"                       },
13607     { titlemessage_initial,             "[title_initial]"               },
13608     { titlemessage,                     "[title]"                       },
13609
13610     // overwrite titles with title screen/message definitions, if defined
13611     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13612     { titlescreen_first,                "[titlescreen]"                 },
13613     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13614     { titlemessage_first,               "[titlemessage]"                },
13615
13616     { titlescreen_initial,              "[titlescreen_initial]"         },
13617     { titlescreen,                      "[titlescreen]"                 },
13618     { titlemessage_initial,             "[titlemessage_initial]"        },
13619     { titlemessage,                     "[titlemessage]"                },
13620
13621     { NULL,                             NULL                            }
13622   };
13623   SetupFileHash *setup_file_hash;
13624   int i, j, k;
13625
13626   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13627     return;
13628
13629   // the following initializes hierarchical values from dynamic configuration
13630
13631   // special case: initialize with default values that may be overwritten
13632   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13633   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13634   {
13635     struct TokenIntPtrInfo menu_config[] =
13636     {
13637       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13638       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13639       { "menu.list_size",       &menu.list_size[i]      }
13640     };
13641
13642     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13643     {
13644       char *token = menu_config[j].token;
13645       char *value = getHashEntry(setup_file_hash, token);
13646
13647       if (value != NULL)
13648         *menu_config[j].value = get_integer_from_string(value);
13649     }
13650   }
13651
13652   // special case: initialize with default values that may be overwritten
13653   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13654   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13655   {
13656     struct TokenIntPtrInfo menu_config[] =
13657     {
13658       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13659       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13660       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13661       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13662       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13663     };
13664
13665     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13666     {
13667       char *token = menu_config[j].token;
13668       char *value = getHashEntry(setup_file_hash, token);
13669
13670       if (value != NULL)
13671         *menu_config[j].value = get_integer_from_string(value);
13672     }
13673   }
13674
13675   // special case: initialize with default values that may be overwritten
13676   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13677   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13678   {
13679     struct TokenIntPtrInfo menu_config[] =
13680     {
13681       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13682       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13683     };
13684
13685     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13686     {
13687       char *token = menu_config[j].token;
13688       char *value = getHashEntry(setup_file_hash, token);
13689
13690       if (value != NULL)
13691         *menu_config[j].value = get_integer_from_string(value);
13692     }
13693   }
13694
13695   // special case: initialize with default values that may be overwritten
13696   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13697   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13698   {
13699     struct TokenIntPtrInfo menu_config[] =
13700     {
13701       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13702       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13703       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13704       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13705       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13706       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13707       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13708       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13709       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13710       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13711     };
13712
13713     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13714     {
13715       char *token = menu_config[j].token;
13716       char *value = getHashEntry(setup_file_hash, token);
13717
13718       if (value != NULL)
13719         *menu_config[j].value = get_integer_from_string(value);
13720     }
13721   }
13722
13723   // special case: initialize with default values that may be overwritten
13724   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13725   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13726   {
13727     struct TokenIntPtrInfo menu_config[] =
13728     {
13729       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13730       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13731       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13732       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13733       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13734       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13735       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13736       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13737       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13738     };
13739
13740     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13741     {
13742       char *token = menu_config[j].token;
13743       char *value = getHashEntry(setup_file_hash, token);
13744
13745       if (value != NULL)
13746         *menu_config[j].value = get_token_parameter_value(token, value);
13747     }
13748   }
13749
13750   // special case: initialize with default values that may be overwritten
13751   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13752   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13753   {
13754     struct
13755     {
13756       char *token_prefix;
13757       struct RectWithBorder *struct_ptr;
13758     }
13759     vp_struct[] =
13760     {
13761       { "viewport.window",      &viewport.window[i]     },
13762       { "viewport.playfield",   &viewport.playfield[i]  },
13763       { "viewport.door_1",      &viewport.door_1[i]     },
13764       { "viewport.door_2",      &viewport.door_2[i]     }
13765     };
13766
13767     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13768     {
13769       struct TokenIntPtrInfo vp_config[] =
13770       {
13771         { ".x",                 &vp_struct[j].struct_ptr->x             },
13772         { ".y",                 &vp_struct[j].struct_ptr->y             },
13773         { ".width",             &vp_struct[j].struct_ptr->width         },
13774         { ".height",            &vp_struct[j].struct_ptr->height        },
13775         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13776         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13777         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13778         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13779         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13780         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13781         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13782         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13783         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13784         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13785         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13786         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13787         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13788         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13789         { ".align",             &vp_struct[j].struct_ptr->align         },
13790         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13791       };
13792
13793       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13794       {
13795         char *token = getStringCat2(vp_struct[j].token_prefix,
13796                                     vp_config[k].token);
13797         char *value = getHashEntry(setup_file_hash, token);
13798
13799         if (value != NULL)
13800           *vp_config[k].value = get_token_parameter_value(token, value);
13801
13802         free(token);
13803       }
13804     }
13805   }
13806
13807   // special case: initialize with default values that may be overwritten
13808   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13809   for (i = 0; title_info[i].info != NULL; i++)
13810   {
13811     struct TitleFadingInfo *info = title_info[i].info;
13812     char *base_token = title_info[i].text;
13813
13814     for (j = 0; title_tokens[j].type != -1; j++)
13815     {
13816       char *token = getStringCat2(base_token, title_tokens[j].text);
13817       char *value = getHashEntry(setup_file_hash, token);
13818
13819       if (value != NULL)
13820       {
13821         int parameter_value = get_token_parameter_value(token, value);
13822
13823         tfi = *info;
13824
13825         *(int *)title_tokens[j].value = (int)parameter_value;
13826
13827         *info = tfi;
13828       }
13829
13830       free(token);
13831     }
13832   }
13833
13834   // special case: initialize with default values that may be overwritten
13835   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13836   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13837   {
13838     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13839     char *base_token = titlemessage_arrays[i].text;
13840
13841     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13842     {
13843       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13844       char *value = getHashEntry(setup_file_hash, token);
13845
13846       if (value != NULL)
13847       {
13848         int parameter_value = get_token_parameter_value(token, value);
13849
13850         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13851         {
13852           tmi = array[k];
13853
13854           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13855             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13856           else
13857             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13858
13859           array[k] = tmi;
13860         }
13861       }
13862
13863       free(token);
13864     }
13865   }
13866
13867   // read (and overwrite with) values that may be specified in config file
13868   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13869
13870   // special case: check if network and preview player positions are redefined
13871   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13872
13873   freeSetupFileHash(setup_file_hash);
13874 }
13875
13876 void LoadMenuDesignSettings(void)
13877 {
13878   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13879
13880   InitMenuDesignSettings_Static();
13881   InitMenuDesignSettings_SpecialPreProcessing();
13882   InitMenuDesignSettings_PreviewPlayers();
13883
13884   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13885   {
13886     // first look for special settings configured in level series config
13887     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13888
13889     if (fileExists(filename_base))
13890       LoadMenuDesignSettingsFromFilename(filename_base);
13891   }
13892
13893   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13894
13895   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13896     LoadMenuDesignSettingsFromFilename(filename_local);
13897
13898   InitMenuDesignSettings_SpecialPostProcessing();
13899 }
13900
13901 void LoadMenuDesignSettings_AfterGraphics(void)
13902 {
13903   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13904 }
13905
13906 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13907                                 boolean ignore_defaults)
13908 {
13909   int i;
13910
13911   for (i = 0; sound_config_vars[i].token != NULL; i++)
13912   {
13913     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13914
13915     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13916     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13917       continue;
13918
13919     if (value != NULL)
13920       *sound_config_vars[i].value =
13921         get_token_parameter_value(sound_config_vars[i].token, value);
13922   }
13923 }
13924
13925 void InitSoundSettings_Static(void)
13926 {
13927   // always start with reliable default values from static default config
13928   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13929 }
13930
13931 static void LoadSoundSettingsFromFilename(char *filename)
13932 {
13933   SetupFileHash *setup_file_hash;
13934
13935   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13936     return;
13937
13938   // read (and overwrite with) values that may be specified in config file
13939   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13940
13941   freeSetupFileHash(setup_file_hash);
13942 }
13943
13944 void LoadSoundSettings(void)
13945 {
13946   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13947
13948   InitSoundSettings_Static();
13949
13950   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13951   {
13952     // first look for special settings configured in level series config
13953     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13954
13955     if (fileExists(filename_base))
13956       LoadSoundSettingsFromFilename(filename_base);
13957   }
13958
13959   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13960
13961   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13962     LoadSoundSettingsFromFilename(filename_local);
13963 }
13964
13965 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13966 {
13967   char *filename = getEditorSetupFilename();
13968   SetupFileList *setup_file_list, *list;
13969   SetupFileHash *element_hash;
13970   int num_unknown_tokens = 0;
13971   int i;
13972
13973   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13974     return;
13975
13976   element_hash = newSetupFileHash();
13977
13978   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13979     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13980
13981   // determined size may be larger than needed (due to unknown elements)
13982   *num_elements = 0;
13983   for (list = setup_file_list; list != NULL; list = list->next)
13984     (*num_elements)++;
13985
13986   // add space for up to 3 more elements for padding that may be needed
13987   *num_elements += 3;
13988
13989   // free memory for old list of elements, if needed
13990   checked_free(*elements);
13991
13992   // allocate memory for new list of elements
13993   *elements = checked_malloc(*num_elements * sizeof(int));
13994
13995   *num_elements = 0;
13996   for (list = setup_file_list; list != NULL; list = list->next)
13997   {
13998     char *value = getHashEntry(element_hash, list->token);
13999
14000     if (value == NULL)          // try to find obsolete token mapping
14001     {
14002       char *mapped_token = get_mapped_token(list->token);
14003
14004       if (mapped_token != NULL)
14005       {
14006         value = getHashEntry(element_hash, mapped_token);
14007
14008         free(mapped_token);
14009       }
14010     }
14011
14012     if (value != NULL)
14013     {
14014       (*elements)[(*num_elements)++] = atoi(value);
14015     }
14016     else
14017     {
14018       if (num_unknown_tokens == 0)
14019       {
14020         Warn("---");
14021         Warn("unknown token(s) found in config file:");
14022         Warn("- config file: '%s'", filename);
14023
14024         num_unknown_tokens++;
14025       }
14026
14027       Warn("- token: '%s'", list->token);
14028     }
14029   }
14030
14031   if (num_unknown_tokens > 0)
14032     Warn("---");
14033
14034   while (*num_elements % 4)     // pad with empty elements, if needed
14035     (*elements)[(*num_elements)++] = EL_EMPTY;
14036
14037   freeSetupFileList(setup_file_list);
14038   freeSetupFileHash(element_hash);
14039
14040 #if 0
14041   for (i = 0; i < *num_elements; i++)
14042     Debug("editor", "element '%s' [%d]\n",
14043           element_info[(*elements)[i]].token_name, (*elements)[i]);
14044 #endif
14045 }
14046
14047 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14048                                                      boolean is_sound)
14049 {
14050   SetupFileHash *setup_file_hash = NULL;
14051   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14052   char *filename_music, *filename_prefix, *filename_info;
14053   struct
14054   {
14055     char *token;
14056     char **value_ptr;
14057   }
14058   token_to_value_ptr[] =
14059   {
14060     { "title_header",   &tmp_music_file_info.title_header       },
14061     { "artist_header",  &tmp_music_file_info.artist_header      },
14062     { "album_header",   &tmp_music_file_info.album_header       },
14063     { "year_header",    &tmp_music_file_info.year_header        },
14064     { "played_header",  &tmp_music_file_info.played_header      },
14065
14066     { "title",          &tmp_music_file_info.title              },
14067     { "artist",         &tmp_music_file_info.artist             },
14068     { "album",          &tmp_music_file_info.album              },
14069     { "year",           &tmp_music_file_info.year               },
14070     { "played",         &tmp_music_file_info.played             },
14071
14072     { NULL,             NULL                                    },
14073   };
14074   int i;
14075
14076   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14077                     getCustomMusicFilename(basename));
14078
14079   if (filename_music == NULL)
14080     return NULL;
14081
14082   // ---------- try to replace file extension ----------
14083
14084   filename_prefix = getStringCopy(filename_music);
14085   if (strrchr(filename_prefix, '.') != NULL)
14086     *strrchr(filename_prefix, '.') = '\0';
14087   filename_info = getStringCat2(filename_prefix, ".txt");
14088
14089   if (fileExists(filename_info))
14090     setup_file_hash = loadSetupFileHash(filename_info);
14091
14092   free(filename_prefix);
14093   free(filename_info);
14094
14095   if (setup_file_hash == NULL)
14096   {
14097     // ---------- try to add file extension ----------
14098
14099     filename_prefix = getStringCopy(filename_music);
14100     filename_info = getStringCat2(filename_prefix, ".txt");
14101
14102     if (fileExists(filename_info))
14103       setup_file_hash = loadSetupFileHash(filename_info);
14104
14105     free(filename_prefix);
14106     free(filename_info);
14107   }
14108
14109   if (setup_file_hash == NULL)
14110     return NULL;
14111
14112   // ---------- music file info found ----------
14113
14114   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14115
14116   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14117   {
14118     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14119
14120     *token_to_value_ptr[i].value_ptr =
14121       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14122   }
14123
14124   tmp_music_file_info.basename = getStringCopy(basename);
14125   tmp_music_file_info.music = music;
14126   tmp_music_file_info.is_sound = is_sound;
14127
14128   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14129   *new_music_file_info = tmp_music_file_info;
14130
14131   return new_music_file_info;
14132 }
14133
14134 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14135 {
14136   return get_music_file_info_ext(basename, music, FALSE);
14137 }
14138
14139 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14140 {
14141   return get_music_file_info_ext(basename, sound, TRUE);
14142 }
14143
14144 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14145                                      char *basename, boolean is_sound)
14146 {
14147   for (; list != NULL; list = list->next)
14148     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14149       return TRUE;
14150
14151   return FALSE;
14152 }
14153
14154 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14155 {
14156   return music_info_listed_ext(list, basename, FALSE);
14157 }
14158
14159 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14160 {
14161   return music_info_listed_ext(list, basename, TRUE);
14162 }
14163
14164 void LoadMusicInfo(void)
14165 {
14166   int num_music_noconf = getMusicListSize_NoConf();
14167   int num_music = getMusicListSize();
14168   int num_sounds = getSoundListSize();
14169   struct FileInfo *music, *sound;
14170   struct MusicFileInfo *next, **new;
14171
14172   int i;
14173
14174   while (music_file_info != NULL)
14175   {
14176     next = music_file_info->next;
14177
14178     checked_free(music_file_info->basename);
14179
14180     checked_free(music_file_info->title_header);
14181     checked_free(music_file_info->artist_header);
14182     checked_free(music_file_info->album_header);
14183     checked_free(music_file_info->year_header);
14184     checked_free(music_file_info->played_header);
14185
14186     checked_free(music_file_info->title);
14187     checked_free(music_file_info->artist);
14188     checked_free(music_file_info->album);
14189     checked_free(music_file_info->year);
14190     checked_free(music_file_info->played);
14191
14192     free(music_file_info);
14193
14194     music_file_info = next;
14195   }
14196
14197   new = &music_file_info;
14198
14199   // get (configured or unconfigured) music file info for all levels
14200   for (i = leveldir_current->first_level;
14201        i <= leveldir_current->last_level; i++)
14202   {
14203     int music_nr;
14204
14205     if (levelset.music[i] != MUS_UNDEFINED)
14206     {
14207       // get music file info for configured level music
14208       music_nr = levelset.music[i];
14209     }
14210     else if (num_music_noconf > 0)
14211     {
14212       // get music file info for unconfigured level music
14213       int level_pos = i - leveldir_current->first_level;
14214
14215       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14216     }
14217     else
14218     {
14219       continue;
14220     }
14221
14222     char *basename = getMusicInfoEntryFilename(music_nr);
14223
14224     if (basename == NULL)
14225       continue;
14226
14227     if (!music_info_listed(music_file_info, basename))
14228     {
14229       *new = get_music_file_info(basename, music_nr);
14230
14231       if (*new != NULL)
14232         new = &(*new)->next;
14233     }
14234   }
14235
14236   // get music file info for all remaining configured music files
14237   for (i = 0; i < num_music; i++)
14238   {
14239     music = getMusicListEntry(i);
14240
14241     if (music->filename == NULL)
14242       continue;
14243
14244     if (strEqual(music->filename, UNDEFINED_FILENAME))
14245       continue;
14246
14247     // a configured file may be not recognized as music
14248     if (!FileIsMusic(music->filename))
14249       continue;
14250
14251     if (!music_info_listed(music_file_info, music->filename))
14252     {
14253       *new = get_music_file_info(music->filename, i);
14254
14255       if (*new != NULL)
14256         new = &(*new)->next;
14257     }
14258   }
14259
14260   // get sound file info for all configured sound files
14261   for (i = 0; i < num_sounds; i++)
14262   {
14263     sound = getSoundListEntry(i);
14264
14265     if (sound->filename == NULL)
14266       continue;
14267
14268     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14269       continue;
14270
14271     // a configured file may be not recognized as sound
14272     if (!FileIsSound(sound->filename))
14273       continue;
14274
14275     if (!sound_info_listed(music_file_info, sound->filename))
14276     {
14277       *new = get_sound_file_info(sound->filename, i);
14278       if (*new != NULL)
14279         new = &(*new)->next;
14280     }
14281   }
14282
14283   // add pointers to previous list nodes
14284
14285   struct MusicFileInfo *node = music_file_info;
14286
14287   while (node != NULL)
14288   {
14289     if (node->next)
14290       node->next->prev = node;
14291
14292     node = node->next;
14293   }
14294 }
14295
14296 static void add_helpanim_entry(int element, int action, int direction,
14297                                int delay, int *num_list_entries)
14298 {
14299   struct HelpAnimInfo *new_list_entry;
14300   (*num_list_entries)++;
14301
14302   helpanim_info =
14303     checked_realloc(helpanim_info,
14304                     *num_list_entries * sizeof(struct HelpAnimInfo));
14305   new_list_entry = &helpanim_info[*num_list_entries - 1];
14306
14307   new_list_entry->element = element;
14308   new_list_entry->action = action;
14309   new_list_entry->direction = direction;
14310   new_list_entry->delay = delay;
14311 }
14312
14313 static void print_unknown_token(char *filename, char *token, int token_nr)
14314 {
14315   if (token_nr == 0)
14316   {
14317     Warn("---");
14318     Warn("unknown token(s) found in config file:");
14319     Warn("- config file: '%s'", filename);
14320   }
14321
14322   Warn("- token: '%s'", token);
14323 }
14324
14325 static void print_unknown_token_end(int token_nr)
14326 {
14327   if (token_nr > 0)
14328     Warn("---");
14329 }
14330
14331 void LoadHelpAnimInfo(void)
14332 {
14333   char *filename = getHelpAnimFilename();
14334   SetupFileList *setup_file_list = NULL, *list;
14335   SetupFileHash *element_hash, *action_hash, *direction_hash;
14336   int num_list_entries = 0;
14337   int num_unknown_tokens = 0;
14338   int i;
14339
14340   if (fileExists(filename))
14341     setup_file_list = loadSetupFileList(filename);
14342
14343   if (setup_file_list == NULL)
14344   {
14345     // use reliable default values from static configuration
14346     SetupFileList *insert_ptr;
14347
14348     insert_ptr = setup_file_list =
14349       newSetupFileList(helpanim_config[0].token,
14350                        helpanim_config[0].value);
14351
14352     for (i = 1; helpanim_config[i].token; i++)
14353       insert_ptr = addListEntry(insert_ptr,
14354                                 helpanim_config[i].token,
14355                                 helpanim_config[i].value);
14356   }
14357
14358   element_hash   = newSetupFileHash();
14359   action_hash    = newSetupFileHash();
14360   direction_hash = newSetupFileHash();
14361
14362   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14363     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14364
14365   for (i = 0; i < NUM_ACTIONS; i++)
14366     setHashEntry(action_hash, element_action_info[i].suffix,
14367                  i_to_a(element_action_info[i].value));
14368
14369   // do not store direction index (bit) here, but direction value!
14370   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14371     setHashEntry(direction_hash, element_direction_info[i].suffix,
14372                  i_to_a(1 << element_direction_info[i].value));
14373
14374   for (list = setup_file_list; list != NULL; list = list->next)
14375   {
14376     char *element_token, *action_token, *direction_token;
14377     char *element_value, *action_value, *direction_value;
14378     int delay = atoi(list->value);
14379
14380     if (strEqual(list->token, "end"))
14381     {
14382       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14383
14384       continue;
14385     }
14386
14387     /* first try to break element into element/action/direction parts;
14388        if this does not work, also accept combined "element[.act][.dir]"
14389        elements (like "dynamite.active"), which are unique elements */
14390
14391     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14392     {
14393       element_value = getHashEntry(element_hash, list->token);
14394       if (element_value != NULL)        // element found
14395         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14396                            &num_list_entries);
14397       else
14398       {
14399         // no further suffixes found -- this is not an element
14400         print_unknown_token(filename, list->token, num_unknown_tokens++);
14401       }
14402
14403       continue;
14404     }
14405
14406     // token has format "<prefix>.<something>"
14407
14408     action_token = strchr(list->token, '.');    // suffix may be action ...
14409     direction_token = action_token;             // ... or direction
14410
14411     element_token = getStringCopy(list->token);
14412     *strchr(element_token, '.') = '\0';
14413
14414     element_value = getHashEntry(element_hash, element_token);
14415
14416     if (element_value == NULL)          // this is no element
14417     {
14418       element_value = getHashEntry(element_hash, list->token);
14419       if (element_value != NULL)        // combined element found
14420         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14421                            &num_list_entries);
14422       else
14423         print_unknown_token(filename, list->token, num_unknown_tokens++);
14424
14425       free(element_token);
14426
14427       continue;
14428     }
14429
14430     action_value = getHashEntry(action_hash, action_token);
14431
14432     if (action_value != NULL)           // action found
14433     {
14434       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14435                     &num_list_entries);
14436
14437       free(element_token);
14438
14439       continue;
14440     }
14441
14442     direction_value = getHashEntry(direction_hash, direction_token);
14443
14444     if (direction_value != NULL)        // direction found
14445     {
14446       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14447                          &num_list_entries);
14448
14449       free(element_token);
14450
14451       continue;
14452     }
14453
14454     if (strchr(action_token + 1, '.') == NULL)
14455     {
14456       // no further suffixes found -- this is not an action nor direction
14457
14458       element_value = getHashEntry(element_hash, list->token);
14459       if (element_value != NULL)        // combined element found
14460         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14461                            &num_list_entries);
14462       else
14463         print_unknown_token(filename, list->token, num_unknown_tokens++);
14464
14465       free(element_token);
14466
14467       continue;
14468     }
14469
14470     // token has format "<prefix>.<suffix>.<something>"
14471
14472     direction_token = strchr(action_token + 1, '.');
14473
14474     action_token = getStringCopy(action_token);
14475     *strchr(action_token + 1, '.') = '\0';
14476
14477     action_value = getHashEntry(action_hash, action_token);
14478
14479     if (action_value == NULL)           // this is no action
14480     {
14481       element_value = getHashEntry(element_hash, list->token);
14482       if (element_value != NULL)        // combined element found
14483         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14484                            &num_list_entries);
14485       else
14486         print_unknown_token(filename, list->token, num_unknown_tokens++);
14487
14488       free(element_token);
14489       free(action_token);
14490
14491       continue;
14492     }
14493
14494     direction_value = getHashEntry(direction_hash, direction_token);
14495
14496     if (direction_value != NULL)        // direction found
14497     {
14498       add_helpanim_entry(atoi(element_value), atoi(action_value),
14499                          atoi(direction_value), delay, &num_list_entries);
14500
14501       free(element_token);
14502       free(action_token);
14503
14504       continue;
14505     }
14506
14507     // this is no direction
14508
14509     element_value = getHashEntry(element_hash, list->token);
14510     if (element_value != NULL)          // combined element found
14511       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14512                          &num_list_entries);
14513     else
14514       print_unknown_token(filename, list->token, num_unknown_tokens++);
14515
14516     free(element_token);
14517     free(action_token);
14518   }
14519
14520   print_unknown_token_end(num_unknown_tokens);
14521
14522   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14523   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14524
14525   freeSetupFileList(setup_file_list);
14526   freeSetupFileHash(element_hash);
14527   freeSetupFileHash(action_hash);
14528   freeSetupFileHash(direction_hash);
14529
14530 #if 0
14531   for (i = 0; i < num_list_entries; i++)
14532     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14533           EL_NAME(helpanim_info[i].element),
14534           helpanim_info[i].element,
14535           helpanim_info[i].action,
14536           helpanim_info[i].direction,
14537           helpanim_info[i].delay);
14538 #endif
14539 }
14540
14541 void LoadHelpTextInfo(void)
14542 {
14543   char *filename = getHelpTextFilename();
14544   int i;
14545
14546   if (helptext_info != NULL)
14547   {
14548     freeSetupFileHash(helptext_info);
14549     helptext_info = NULL;
14550   }
14551
14552   if (fileExists(filename))
14553     helptext_info = loadSetupFileHash(filename);
14554
14555   if (helptext_info == NULL)
14556   {
14557     // use reliable default values from static configuration
14558     helptext_info = newSetupFileHash();
14559
14560     for (i = 0; helptext_config[i].token; i++)
14561       setHashEntry(helptext_info,
14562                    helptext_config[i].token,
14563                    helptext_config[i].value);
14564   }
14565
14566 #if 0
14567   BEGIN_HASH_ITERATION(helptext_info, itr)
14568   {
14569     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14570           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14571   }
14572   END_HASH_ITERATION(hash, itr)
14573 #endif
14574 }
14575
14576
14577 // ----------------------------------------------------------------------------
14578 // convert levels
14579 // ----------------------------------------------------------------------------
14580
14581 #define MAX_NUM_CONVERT_LEVELS          1000
14582
14583 void ConvertLevels(void)
14584 {
14585   static LevelDirTree *convert_leveldir = NULL;
14586   static int convert_level_nr = -1;
14587   static int num_levels_handled = 0;
14588   static int num_levels_converted = 0;
14589   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14590   int i;
14591
14592   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14593                                                global.convert_leveldir);
14594
14595   if (convert_leveldir == NULL)
14596     Fail("no such level identifier: '%s'", global.convert_leveldir);
14597
14598   leveldir_current = convert_leveldir;
14599
14600   if (global.convert_level_nr != -1)
14601   {
14602     convert_leveldir->first_level = global.convert_level_nr;
14603     convert_leveldir->last_level  = global.convert_level_nr;
14604   }
14605
14606   convert_level_nr = convert_leveldir->first_level;
14607
14608   PrintLine("=", 79);
14609   Print("Converting levels\n");
14610   PrintLine("-", 79);
14611   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14612   Print("Level series name:       '%s'\n", convert_leveldir->name);
14613   Print("Level series author:     '%s'\n", convert_leveldir->author);
14614   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14615   PrintLine("=", 79);
14616   Print("\n");
14617
14618   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14619     levels_failed[i] = FALSE;
14620
14621   while (convert_level_nr <= convert_leveldir->last_level)
14622   {
14623     char *level_filename;
14624     boolean new_level;
14625
14626     level_nr = convert_level_nr++;
14627
14628     Print("Level %03d: ", level_nr);
14629
14630     LoadLevel(level_nr);
14631     if (level.no_level_file || level.no_valid_file)
14632     {
14633       Print("(no level)\n");
14634       continue;
14635     }
14636
14637     Print("converting level ... ");
14638
14639 #if 0
14640     // special case: conversion of some EMC levels as requested by ACME
14641     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14642 #endif
14643
14644     level_filename = getDefaultLevelFilename(level_nr);
14645     new_level = !fileExists(level_filename);
14646
14647     if (new_level)
14648     {
14649       SaveLevel(level_nr);
14650
14651       num_levels_converted++;
14652
14653       Print("converted.\n");
14654     }
14655     else
14656     {
14657       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14658         levels_failed[level_nr] = TRUE;
14659
14660       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14661     }
14662
14663     num_levels_handled++;
14664   }
14665
14666   Print("\n");
14667   PrintLine("=", 79);
14668   Print("Number of levels handled: %d\n", num_levels_handled);
14669   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14670          (num_levels_handled ?
14671           num_levels_converted * 100 / num_levels_handled : 0));
14672   PrintLine("-", 79);
14673   Print("Summary (for automatic parsing by scripts):\n");
14674   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14675          convert_leveldir->identifier, num_levels_converted,
14676          num_levels_handled,
14677          (num_levels_handled ?
14678           num_levels_converted * 100 / num_levels_handled : 0));
14679
14680   if (num_levels_handled != num_levels_converted)
14681   {
14682     Print(", FAILED:");
14683     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14684       if (levels_failed[i])
14685         Print(" %03d", i);
14686   }
14687
14688   Print("\n");
14689   PrintLine("=", 79);
14690
14691   CloseAllAndExit(0);
14692 }
14693
14694
14695 // ----------------------------------------------------------------------------
14696 // create and save images for use in level sketches (raw BMP format)
14697 // ----------------------------------------------------------------------------
14698
14699 void CreateLevelSketchImages(void)
14700 {
14701   Bitmap *bitmap1;
14702   Bitmap *bitmap2;
14703   int i;
14704
14705   InitElementPropertiesGfxElement();
14706
14707   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14708   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14709
14710   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14711   {
14712     int element = getMappedElement(i);
14713     char basename1[16];
14714     char basename2[16];
14715     char *filename1;
14716     char *filename2;
14717
14718     sprintf(basename1, "%04d.bmp", i);
14719     sprintf(basename2, "%04ds.bmp", i);
14720
14721     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14722     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14723
14724     DrawSizedElement(0, 0, element, TILESIZE);
14725     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14726
14727     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14728       Fail("cannot save level sketch image file '%s'", filename1);
14729
14730     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14731     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14732
14733     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14734       Fail("cannot save level sketch image file '%s'", filename2);
14735
14736     free(filename1);
14737     free(filename2);
14738
14739     // create corresponding SQL statements (for normal and small images)
14740     if (i < 1000)
14741     {
14742       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14743       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14744     }
14745
14746     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14747     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14748
14749     // optional: create content for forum level sketch demonstration post
14750     if (options.debug)
14751       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14752   }
14753
14754   FreeBitmap(bitmap1);
14755   FreeBitmap(bitmap2);
14756
14757   if (options.debug)
14758     fprintf(stderr, "\n");
14759
14760   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14761
14762   CloseAllAndExit(0);
14763 }
14764
14765
14766 // ----------------------------------------------------------------------------
14767 // create and save images for element collecting animations (raw BMP format)
14768 // ----------------------------------------------------------------------------
14769
14770 static boolean createCollectImage(int element)
14771 {
14772   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14773 }
14774
14775 void CreateCollectElementImages(void)
14776 {
14777   int i, j;
14778   int num_steps = 8;
14779   int anim_frames = num_steps - 1;
14780   int tile_size = TILESIZE;
14781   int anim_width  = tile_size * anim_frames;
14782   int anim_height = tile_size;
14783   int num_collect_images = 0;
14784   int pos_collect_images = 0;
14785
14786   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14787     if (createCollectImage(i))
14788       num_collect_images++;
14789
14790   Info("Creating %d element collecting animation images ...",
14791        num_collect_images);
14792
14793   int dst_width  = anim_width * 2;
14794   int dst_height = anim_height * num_collect_images / 2;
14795   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14796   char *basename_bmp = "RocksCollect.bmp";
14797   char *basename_png = "RocksCollect.png";
14798   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14799   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14800   int len_filename_bmp = strlen(filename_bmp);
14801   int len_filename_png = strlen(filename_png);
14802   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14803   char cmd_convert[max_command_len];
14804
14805   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14806            filename_bmp,
14807            filename_png);
14808
14809   // force using RGBA surface for destination bitmap
14810   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14811                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14812
14813   dst_bitmap->surface =
14814     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14815
14816   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14817   {
14818     if (!createCollectImage(i))
14819       continue;
14820
14821     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14822     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14823     int graphic = el2img(i);
14824     char *token_name = element_info[i].token_name;
14825     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14826     Bitmap *src_bitmap;
14827     int src_x, src_y;
14828
14829     Info("- creating collecting image for '%s' ...", token_name);
14830
14831     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14832
14833     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14834                tile_size, tile_size, 0, 0);
14835
14836     // force using RGBA surface for temporary bitmap (using transparent black)
14837     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14838                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14839
14840     tmp_bitmap->surface =
14841       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14842
14843     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14844
14845     for (j = 0; j < anim_frames; j++)
14846     {
14847       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14848       int frame_size = frame_size_final * num_steps;
14849       int offset = (tile_size - frame_size_final) / 2;
14850       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14851
14852       while (frame_size > frame_size_final)
14853       {
14854         frame_size /= 2;
14855
14856         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14857
14858         FreeBitmap(frame_bitmap);
14859
14860         frame_bitmap = half_bitmap;
14861       }
14862
14863       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14864                        frame_size_final, frame_size_final,
14865                        dst_x + j * tile_size + offset, dst_y + offset);
14866
14867       FreeBitmap(frame_bitmap);
14868     }
14869
14870     tmp_bitmap->surface_masked = NULL;
14871
14872     FreeBitmap(tmp_bitmap);
14873
14874     pos_collect_images++;
14875   }
14876
14877   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14878     Fail("cannot save element collecting image file '%s'", filename_bmp);
14879
14880   FreeBitmap(dst_bitmap);
14881
14882   Info("Converting image file from BMP to PNG ...");
14883
14884   if (system(cmd_convert) != 0)
14885     Fail("converting image file failed");
14886
14887   unlink(filename_bmp);
14888
14889   Info("Done.");
14890
14891   CloseAllAndExit(0);
14892 }
14893
14894
14895 // ----------------------------------------------------------------------------
14896 // create and save images for custom and group elements (raw BMP format)
14897 // ----------------------------------------------------------------------------
14898
14899 void CreateCustomElementImages(char *directory)
14900 {
14901   char *src_basename = "RocksCE-template.ilbm";
14902   char *dst_basename = "RocksCE.bmp";
14903   char *src_filename = getPath2(directory, src_basename);
14904   char *dst_filename = getPath2(directory, dst_basename);
14905   Bitmap *src_bitmap;
14906   Bitmap *bitmap;
14907   int yoffset_ce = 0;
14908   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14909   int i;
14910
14911   InitVideoDefaults();
14912
14913   ReCreateBitmap(&backbuffer, video.width, video.height);
14914
14915   src_bitmap = LoadImage(src_filename);
14916
14917   bitmap = CreateBitmap(TILEX * 16 * 2,
14918                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14919                         DEFAULT_DEPTH);
14920
14921   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14922   {
14923     int x = i % 16;
14924     int y = i / 16;
14925     int ii = i + 1;
14926     int j;
14927
14928     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14929                TILEX * x, TILEY * y + yoffset_ce);
14930
14931     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14932                TILEX, TILEY,
14933                TILEX * x + TILEX * 16,
14934                TILEY * y + yoffset_ce);
14935
14936     for (j = 2; j >= 0; j--)
14937     {
14938       int c = ii % 10;
14939
14940       BlitBitmap(src_bitmap, bitmap,
14941                  TILEX + c * 7, 0, 6, 10,
14942                  TILEX * x + 6 + j * 7,
14943                  TILEY * y + 11 + yoffset_ce);
14944
14945       BlitBitmap(src_bitmap, bitmap,
14946                  TILEX + c * 8, TILEY, 6, 10,
14947                  TILEX * 16 + TILEX * x + 6 + j * 8,
14948                  TILEY * y + 10 + yoffset_ce);
14949
14950       ii /= 10;
14951     }
14952   }
14953
14954   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14955   {
14956     int x = i % 16;
14957     int y = i / 16;
14958     int ii = i + 1;
14959     int j;
14960
14961     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14962                TILEX * x, TILEY * y + yoffset_ge);
14963
14964     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14965                TILEX, TILEY,
14966                TILEX * x + TILEX * 16,
14967                TILEY * y + yoffset_ge);
14968
14969     for (j = 1; j >= 0; j--)
14970     {
14971       int c = ii % 10;
14972
14973       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14974                  TILEX * x + 6 + j * 10,
14975                  TILEY * y + 11 + yoffset_ge);
14976
14977       BlitBitmap(src_bitmap, bitmap,
14978                  TILEX + c * 8, TILEY + 12, 6, 10,
14979                  TILEX * 16 + TILEX * x + 10 + j * 8,
14980                  TILEY * y + 10 + yoffset_ge);
14981
14982       ii /= 10;
14983     }
14984   }
14985
14986   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14987     Fail("cannot save CE graphics file '%s'", dst_filename);
14988
14989   FreeBitmap(bitmap);
14990
14991   CloseAllAndExit(0);
14992 }