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