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