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