added support for conveyor belt settings in BD engine to level editor
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313   {
314     -1,                                 -1,
315     TYPE_INTEGER,                       CONF_VALUE_8_BIT(24),
316     &li.bd_cave_random_seed_c64,        0
317   },
318
319   {
320     -1,                                 -1,
321     -1,                                 -1,
322     NULL,                               -1
323   }
324 };
325
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
327 {
328   // (these values are the same for each player)
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
332     &li.block_last_field,               FALSE   // default case for EM levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
337     &li.sp_block_last_field,            TRUE    // default case for SP levels
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
342     &li.instant_relocation,             FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
347     &li.can_pass_to_walkable,           FALSE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
352     &li.block_snap_field,               TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
357     &li.continuous_snapping,            TRUE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
362     &li.shifted_relocation,             FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
367     &li.lazy_relocation,                FALSE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
372     &li.finish_dig_collect,             TRUE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
377     &li.keep_walkable_ce,               FALSE
378   },
379
380   // (these values are different for each player)
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
384     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
389     &li.initial_player_gravity[0],      FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
394     &li.use_start_element[0],           FALSE
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
399     &li.start_element[0],               EL_PLAYER_1
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
404     &li.use_artwork_element[0],         FALSE
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
409     &li.artwork_element[0],             EL_PLAYER_1
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
414     &li.use_explosion_element[0],       FALSE
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
419     &li.explosion_element[0],           EL_PLAYER_1
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
424     &li.use_initial_inventory[0],       FALSE
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
429     &li.initial_inventory_size[0],      1
430   },
431   {
432     EL_PLAYER_1,                        -1,
433     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
434     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
436   },
437
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
441     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
446     &li.initial_player_gravity[1],      FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
451     &li.use_start_element[1],           FALSE
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
456     &li.start_element[1],               EL_PLAYER_2
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
461     &li.use_artwork_element[1],         FALSE
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
466     &li.artwork_element[1],             EL_PLAYER_2
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
471     &li.use_explosion_element[1],       FALSE
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
476     &li.explosion_element[1],           EL_PLAYER_2
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
481     &li.use_initial_inventory[1],       FALSE
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
486     &li.initial_inventory_size[1],      1
487   },
488   {
489     EL_PLAYER_2,                        -1,
490     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
491     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
493   },
494
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
498     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
503     &li.initial_player_gravity[2],      FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
508     &li.use_start_element[2],           FALSE
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
513     &li.start_element[2],               EL_PLAYER_3
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
518     &li.use_artwork_element[2],         FALSE
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
523     &li.artwork_element[2],             EL_PLAYER_3
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
528     &li.use_explosion_element[2],       FALSE
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
533     &li.explosion_element[2],           EL_PLAYER_3
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
538     &li.use_initial_inventory[2],       FALSE
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
543     &li.initial_inventory_size[2],      1
544   },
545   {
546     EL_PLAYER_3,                        -1,
547     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
548     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
550   },
551
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
555     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
560     &li.initial_player_gravity[3],      FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
565     &li.use_start_element[3],           FALSE
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
570     &li.start_element[3],               EL_PLAYER_4
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
575     &li.use_artwork_element[3],         FALSE
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
580     &li.artwork_element[3],             EL_PLAYER_4
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
585     &li.use_explosion_element[3],       FALSE
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
590     &li.explosion_element[3],           EL_PLAYER_4
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
595     &li.use_initial_inventory[3],       FALSE
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
600     &li.initial_inventory_size[3],      1
601   },
602   {
603     EL_PLAYER_4,                        -1,
604     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
605     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
607   },
608
609   // (these values are only valid for BD style levels)
610   // (some values for BD style amoeba following below)
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
614     &li.bd_diagonal_movements,          FALSE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
619     &li.bd_topmost_player_active,       TRUE
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
624     &li.bd_pushing_prob,                25
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
629     &li.bd_pushing_prob_with_sweet,     100
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
634     &li.bd_push_mega_rock_with_sweet,   FALSE
635   },
636   {
637     EL_BD_PLAYER,                       -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
639     &li.bd_snap_element,                EL_EMPTY
640   },
641
642   {
643     EL_BD_DIAMOND,                      -1,
644     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
645     &li.score[SC_DIAMOND_EXTRA],        20
646   },
647
648   {
649     EL_BD_MAGIC_WALL,                   -1,
650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
651     &li.bd_magic_wall_wait_hatching,    FALSE
652   },
653   {
654     EL_BD_MAGIC_WALL,                   -1,
655     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
656     &li.bd_magic_wall_stops_amoeba,     TRUE
657   },
658
659   {
660     EL_BD_CLOCK,                        -1,
661     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
662     &li.bd_clock_extra_time,            30
663   },
664
665   {
666     EL_BD_VOODOO_DOLL,                  -1,
667     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
668     &li.bd_voodoo_collects_diamonds,    FALSE
669   },
670   {
671     EL_BD_VOODOO_DOLL,                  -1,
672     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
673     &li.bd_voodoo_hurt_kills_player,    FALSE
674   },
675   {
676     EL_BD_VOODOO_DOLL,                  -1,
677     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
678     &li.bd_voodoo_dies_by_rock,         FALSE
679   },
680   {
681     EL_BD_VOODOO_DOLL,                  -1,
682     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
683     &li.bd_voodoo_vanish_by_explosion,  TRUE
684   },
685   {
686     EL_BD_VOODOO_DOLL,                  -1,
687     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
688     &li.bd_voodoo_penalty_time,         30
689   },
690
691   {
692     EL_BD_SLIME,                        -1,
693     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
694     &li.bd_slime_is_predictable,        TRUE
695   },
696   {
697     EL_BD_SLIME,                        -1,
698     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
699     &li.bd_slime_permeability_rate,     100
700   },
701   {
702     EL_BD_SLIME,                        -1,
703     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
704     &li.bd_slime_permeability_bits_c64, 0
705   },
706   {
707     EL_BD_SLIME,                        -1,
708     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
709     &li.bd_slime_random_seed_c64,       -1
710   },
711
712   {
713     EL_BD_ACID,                         -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_acid_eats_element,           EL_BD_SAND
716   },
717   {
718     EL_BD_ACID,                         -1,
719     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
720     &li.bd_acid_spread_rate,            3
721   },
722   {
723     EL_BD_ACID,                         -1,
724     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
725     &li.bd_acid_turns_to_element,       EL_EMPTY
726   },
727
728   {
729     EL_BD_BITER,                        -1,
730     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
731     &li.bd_biter_move_delay,            0
732   },
733   {
734     EL_BD_BITER,                        -1,
735     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
736     &li.bd_biter_eats_element,          EL_BD_DIAMOND
737   },
738
739   {
740     EL_BD_BLADDER,                      -1,
741     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
742     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
743   },
744
745   {
746     EL_BD_EXPANDABLE_WALL_ANY,          -1,
747     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
748     &li.bd_change_expanding_wall,       FALSE
749   },
750
751   {
752     EL_BD_REPLICATOR,                   -1,
753     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
754     &li.bd_replicators_active,          TRUE
755   },
756   {
757     EL_BD_REPLICATOR,                   -1,
758     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
759     &li.bd_replicator_create_delay,     4
760   },
761
762   {
763     EL_BD_CONVEYOR_LEFT,                -1,
764     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
765     &li.bd_conveyor_belts_active,       TRUE
766   },
767   {
768     EL_BD_CONVEYOR_LEFT,                -1,
769     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
770     &li.bd_conveyor_belts_changed,      FALSE
771   },
772
773   // (the following values are related to various game elements)
774
775   {
776     EL_EMERALD,                         -1,
777     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
778     &li.score[SC_EMERALD],              10
779   },
780
781   {
782     EL_DIAMOND,                         -1,
783     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
784     &li.score[SC_DIAMOND],              10
785   },
786
787   {
788     EL_BUG,                             -1,
789     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
790     &li.score[SC_BUG],                  10
791   },
792
793   {
794     EL_SPACESHIP,                       -1,
795     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
796     &li.score[SC_SPACESHIP],            10
797   },
798
799   {
800     EL_PACMAN,                          -1,
801     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
802     &li.score[SC_PACMAN],               10
803   },
804
805   {
806     EL_NUT,                             -1,
807     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
808     &li.score[SC_NUT],                  10
809   },
810
811   {
812     EL_DYNAMITE,                        -1,
813     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
814     &li.score[SC_DYNAMITE],             10
815   },
816
817   {
818     EL_KEY_1,                           -1,
819     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
820     &li.score[SC_KEY],                  10
821   },
822
823   {
824     EL_PEARL,                           -1,
825     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
826     &li.score[SC_PEARL],                10
827   },
828
829   {
830     EL_CRYSTAL,                         -1,
831     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
832     &li.score[SC_CRYSTAL],              10
833   },
834
835   // (amoeba values used by R'n'D game engine only)
836   {
837     EL_BD_AMOEBA,                       -1,
838     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
839     &li.amoeba_content,                 EL_DIAMOND
840   },
841   {
842     EL_BD_AMOEBA,                       -1,
843     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
844     &li.amoeba_speed,                   10
845   },
846   {
847     EL_BD_AMOEBA,                       -1,
848     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
849     &li.grow_into_diggable,             TRUE
850   },
851   // (amoeba values used by BD game engine only)
852   {
853     EL_BD_AMOEBA,                       -1,
854     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
855     &li.bd_amoeba_wait_for_hatching,    FALSE
856   },
857   {
858     EL_BD_AMOEBA,                       -1,
859     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
860     &li.bd_amoeba_start_immediately,    TRUE
861   },
862   {
863     EL_BD_AMOEBA,                       -1,
864     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
865     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
866   },
867   {
868     EL_BD_AMOEBA,                       -1,
869     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
870     &li.bd_amoeba_threshold_too_big,    200
871   },
872   {
873     EL_BD_AMOEBA,                       -1,
874     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
875     &li.bd_amoeba_slow_growth_time,     200
876   },
877   {
878     EL_BD_AMOEBA,                       -1,
879     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
880     &li.bd_amoeba_slow_growth_rate,     3
881   },
882   {
883     EL_BD_AMOEBA,                       -1,
884     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
885     &li.bd_amoeba_fast_growth_rate,     25
886   },
887   {
888     EL_BD_AMOEBA,                       -1,
889     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
890     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
891   },
892   {
893     EL_BD_AMOEBA,                       -1,
894     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
895     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
896   },
897
898   {
899     EL_BD_AMOEBA_2,                     -1,
900     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
901     &li.bd_amoeba_2_threshold_too_big,  200
902   },
903   {
904     EL_BD_AMOEBA_2,                     -1,
905     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
906     &li.bd_amoeba_2_slow_growth_time,   200
907   },
908   {
909     EL_BD_AMOEBA_2,                     -1,
910     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
911     &li.bd_amoeba_2_slow_growth_rate,   3
912   },
913   {
914     EL_BD_AMOEBA_2,                     -1,
915     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
916     &li.bd_amoeba_2_fast_growth_rate,   25
917   },
918   {
919     EL_BD_AMOEBA_2,                     -1,
920     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
921     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
922   },
923   {
924     EL_BD_AMOEBA_2,                     -1,
925     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
926     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
927   },
928   {
929     EL_BD_AMOEBA_2,                     -1,
930     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
931     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
932   },
933   {
934     EL_BD_AMOEBA_2,                     -1,
935     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
936     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
937   },
938
939   {
940     EL_YAMYAM,                          -1,
941     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
942     &li.yamyam_content,                 EL_ROCK, NULL,
943     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
944   },
945   {
946     EL_YAMYAM,                          -1,
947     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
948     &li.score[SC_YAMYAM],               10
949   },
950
951   {
952     EL_ROBOT,                           -1,
953     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
954     &li.score[SC_ROBOT],                10
955   },
956   {
957     EL_ROBOT,                           -1,
958     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
959     &li.slurp_score,                    10
960   },
961
962   {
963     EL_ROBOT_WHEEL,                     -1,
964     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
965     &li.time_wheel,                     10
966   },
967
968   {
969     EL_MAGIC_WALL,                      -1,
970     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
971     &li.time_magic_wall,                10
972   },
973
974   {
975     EL_GAME_OF_LIFE,                    -1,
976     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
977     &li.game_of_life[0],                2
978   },
979   {
980     EL_GAME_OF_LIFE,                    -1,
981     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
982     &li.game_of_life[1],                3
983   },
984   {
985     EL_GAME_OF_LIFE,                    -1,
986     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
987     &li.game_of_life[2],                3
988   },
989   {
990     EL_GAME_OF_LIFE,                    -1,
991     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
992     &li.game_of_life[3],                3
993   },
994   {
995     EL_GAME_OF_LIFE,                    -1,
996     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
997     &li.use_life_bugs,                  FALSE
998   },
999
1000   {
1001     EL_BIOMAZE,                         -1,
1002     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1003     &li.biomaze[0],                     2
1004   },
1005   {
1006     EL_BIOMAZE,                         -1,
1007     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1008     &li.biomaze[1],                     3
1009   },
1010   {
1011     EL_BIOMAZE,                         -1,
1012     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1013     &li.biomaze[2],                     3
1014   },
1015   {
1016     EL_BIOMAZE,                         -1,
1017     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1018     &li.biomaze[3],                     3
1019   },
1020
1021   {
1022     EL_TIMEGATE_SWITCH,                 -1,
1023     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1024     &li.time_timegate,                  10
1025   },
1026
1027   {
1028     EL_LIGHT_SWITCH_ACTIVE,             -1,
1029     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1030     &li.time_light,                     10
1031   },
1032
1033   {
1034     EL_SHIELD_NORMAL,                   -1,
1035     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1036     &li.shield_normal_time,             10
1037   },
1038   {
1039     EL_SHIELD_NORMAL,                   -1,
1040     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1041     &li.score[SC_SHIELD],               10
1042   },
1043
1044   {
1045     EL_SHIELD_DEADLY,                   -1,
1046     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1047     &li.shield_deadly_time,             10
1048   },
1049   {
1050     EL_SHIELD_DEADLY,                   -1,
1051     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1052     &li.score[SC_SHIELD],               10
1053   },
1054
1055   {
1056     EL_EXTRA_TIME,                      -1,
1057     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1058     &li.extra_time,                     10
1059   },
1060   {
1061     EL_EXTRA_TIME,                      -1,
1062     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1063     &li.extra_time_score,               10
1064   },
1065
1066   {
1067     EL_TIME_ORB_FULL,                   -1,
1068     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1069     &li.time_orb_time,                  10
1070   },
1071   {
1072     EL_TIME_ORB_FULL,                   -1,
1073     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1074     &li.use_time_orb_bug,               FALSE
1075   },
1076
1077   {
1078     EL_SPRING,                          -1,
1079     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1080     &li.use_spring_bug,                 FALSE
1081   },
1082
1083   {
1084     EL_EMC_ANDROID,                     -1,
1085     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1086     &li.android_move_time,              10
1087   },
1088   {
1089     EL_EMC_ANDROID,                     -1,
1090     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1091     &li.android_clone_time,             10
1092   },
1093   {
1094     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1095     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1096     &li.android_clone_element[0],       EL_EMPTY, NULL,
1097     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1098   },
1099   {
1100     EL_EMC_ANDROID,                     -1,
1101     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1102     &li.android_clone_element[0],       EL_EMPTY, NULL,
1103     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1104   },
1105
1106   {
1107     EL_EMC_LENSES,                      -1,
1108     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1109     &li.lenses_score,                   10
1110   },
1111   {
1112     EL_EMC_LENSES,                      -1,
1113     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1114     &li.lenses_time,                    10
1115   },
1116
1117   {
1118     EL_EMC_MAGNIFIER,                   -1,
1119     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1120     &li.magnify_score,                  10
1121   },
1122   {
1123     EL_EMC_MAGNIFIER,                   -1,
1124     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1125     &li.magnify_time,                   10
1126   },
1127
1128   {
1129     EL_EMC_MAGIC_BALL,                  -1,
1130     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1131     &li.ball_time,                      10
1132   },
1133   {
1134     EL_EMC_MAGIC_BALL,                  -1,
1135     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1136     &li.ball_random,                    FALSE
1137   },
1138   {
1139     EL_EMC_MAGIC_BALL,                  -1,
1140     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1141     &li.ball_active_initial,            FALSE
1142   },
1143   {
1144     EL_EMC_MAGIC_BALL,                  -1,
1145     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1146     &li.ball_content,                   EL_EMPTY, NULL,
1147     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1148   },
1149
1150   {
1151     EL_SOKOBAN_FIELD_EMPTY,             -1,
1152     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1153     &li.sb_fields_needed,               TRUE
1154   },
1155
1156   {
1157     EL_SOKOBAN_OBJECT,                  -1,
1158     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1159     &li.sb_objects_needed,              TRUE
1160   },
1161
1162   {
1163     EL_MM_MCDUFFIN,                     -1,
1164     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1165     &li.mm_laser_red,                   FALSE
1166   },
1167   {
1168     EL_MM_MCDUFFIN,                     -1,
1169     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1170     &li.mm_laser_green,                 FALSE
1171   },
1172   {
1173     EL_MM_MCDUFFIN,                     -1,
1174     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1175     &li.mm_laser_blue,                  TRUE
1176   },
1177
1178   {
1179     EL_DF_LASER,                        -1,
1180     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1181     &li.df_laser_red,                   TRUE
1182   },
1183   {
1184     EL_DF_LASER,                        -1,
1185     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1186     &li.df_laser_green,                 TRUE
1187   },
1188   {
1189     EL_DF_LASER,                        -1,
1190     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1191     &li.df_laser_blue,                  FALSE
1192   },
1193
1194   {
1195     EL_MM_FUSE_ACTIVE,                  -1,
1196     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1197     &li.mm_time_fuse,                   25
1198   },
1199   {
1200     EL_MM_BOMB,                         -1,
1201     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1202     &li.mm_time_bomb,                   75
1203   },
1204
1205   {
1206     EL_MM_GRAY_BALL,                    -1,
1207     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1208     &li.mm_time_ball,                   75
1209   },
1210   {
1211     EL_MM_GRAY_BALL,                    -1,
1212     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1213     &li.mm_ball_choice_mode,            ANIM_RANDOM
1214   },
1215   {
1216     EL_MM_GRAY_BALL,                    -1,
1217     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1218     &li.mm_ball_content,                EL_EMPTY, NULL,
1219     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1220   },
1221   {
1222     EL_MM_GRAY_BALL,                    -1,
1223     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1224     &li.rotate_mm_ball_content,         TRUE
1225   },
1226   {
1227     EL_MM_GRAY_BALL,                    -1,
1228     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1229     &li.explode_mm_ball,                FALSE
1230   },
1231
1232   {
1233     EL_MM_STEEL_BLOCK,                  -1,
1234     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1235     &li.mm_time_block,                  75
1236   },
1237   {
1238     EL_MM_LIGHTBALL,                    -1,
1239     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1240     &li.score[SC_ELEM_BONUS],           10
1241   },
1242
1243   {
1244     -1,                                 -1,
1245     -1,                                 -1,
1246     NULL,                               -1
1247   }
1248 };
1249
1250 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1251 {
1252   {
1253     -1,                                 -1,
1254     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1255     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1256   },
1257   {
1258     -1,                                 -1,
1259     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1260     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1261   },
1262
1263   {
1264     -1,                                 -1,
1265     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1266     &xx_envelope.autowrap,              FALSE
1267   },
1268   {
1269     -1,                                 -1,
1270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1271     &xx_envelope.centered,              FALSE
1272   },
1273
1274   {
1275     -1,                                 -1,
1276     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1277     &xx_envelope.text,                  -1, NULL,
1278     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1279     &xx_default_string_empty[0]
1280   },
1281
1282   {
1283     -1,                                 -1,
1284     -1,                                 -1,
1285     NULL,                               -1
1286   }
1287 };
1288
1289 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1290 {
1291   {
1292     -1,                                 -1,
1293     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1294     &xx_ei.description[0],              -1,
1295     &yy_ei.description[0],
1296     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1297     &xx_default_description[0]
1298   },
1299
1300   {
1301     -1,                                 -1,
1302     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1303     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1304     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1305   },
1306 #if ENABLE_RESERVED_CODE
1307   // (reserved for later use)
1308   {
1309     -1,                                 -1,
1310     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1311     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1312     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1313   },
1314 #endif
1315
1316   {
1317     -1,                                 -1,
1318     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1319     &xx_ei.use_gfx_element,             FALSE,
1320     &yy_ei.use_gfx_element
1321   },
1322   {
1323     -1,                                 -1,
1324     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1325     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1326     &yy_ei.gfx_element_initial
1327   },
1328
1329   {
1330     -1,                                 -1,
1331     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1332     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1333     &yy_ei.access_direction
1334   },
1335
1336   {
1337     -1,                                 -1,
1338     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1339     &xx_ei.collect_score_initial,       10,
1340     &yy_ei.collect_score_initial
1341   },
1342   {
1343     -1,                                 -1,
1344     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1345     &xx_ei.collect_count_initial,       1,
1346     &yy_ei.collect_count_initial
1347   },
1348
1349   {
1350     -1,                                 -1,
1351     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1352     &xx_ei.ce_value_fixed_initial,      0,
1353     &yy_ei.ce_value_fixed_initial
1354   },
1355   {
1356     -1,                                 -1,
1357     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1358     &xx_ei.ce_value_random_initial,     0,
1359     &yy_ei.ce_value_random_initial
1360   },
1361   {
1362     -1,                                 -1,
1363     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1364     &xx_ei.use_last_ce_value,           FALSE,
1365     &yy_ei.use_last_ce_value
1366   },
1367
1368   {
1369     -1,                                 -1,
1370     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1371     &xx_ei.push_delay_fixed,            8,
1372     &yy_ei.push_delay_fixed
1373   },
1374   {
1375     -1,                                 -1,
1376     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1377     &xx_ei.push_delay_random,           8,
1378     &yy_ei.push_delay_random
1379   },
1380   {
1381     -1,                                 -1,
1382     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1383     &xx_ei.drop_delay_fixed,            0,
1384     &yy_ei.drop_delay_fixed
1385   },
1386   {
1387     -1,                                 -1,
1388     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1389     &xx_ei.drop_delay_random,           0,
1390     &yy_ei.drop_delay_random
1391   },
1392   {
1393     -1,                                 -1,
1394     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1395     &xx_ei.move_delay_fixed,            0,
1396     &yy_ei.move_delay_fixed
1397   },
1398   {
1399     -1,                                 -1,
1400     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1401     &xx_ei.move_delay_random,           0,
1402     &yy_ei.move_delay_random
1403   },
1404   {
1405     -1,                                 -1,
1406     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1407     &xx_ei.step_delay_fixed,            0,
1408     &yy_ei.step_delay_fixed
1409   },
1410   {
1411     -1,                                 -1,
1412     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1413     &xx_ei.step_delay_random,           0,
1414     &yy_ei.step_delay_random
1415   },
1416
1417   {
1418     -1,                                 -1,
1419     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1420     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1421     &yy_ei.move_pattern
1422   },
1423   {
1424     -1,                                 -1,
1425     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1426     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1427     &yy_ei.move_direction_initial
1428   },
1429   {
1430     -1,                                 -1,
1431     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1432     &xx_ei.move_stepsize,               TILEX / 8,
1433     &yy_ei.move_stepsize
1434   },
1435
1436   {
1437     -1,                                 -1,
1438     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1439     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1440     &yy_ei.move_enter_element
1441   },
1442   {
1443     -1,                                 -1,
1444     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1445     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1446     &yy_ei.move_leave_element
1447   },
1448   {
1449     -1,                                 -1,
1450     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1451     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1452     &yy_ei.move_leave_type
1453   },
1454
1455   {
1456     -1,                                 -1,
1457     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1458     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1459     &yy_ei.slippery_type
1460   },
1461
1462   {
1463     -1,                                 -1,
1464     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1465     &xx_ei.explosion_type,              EXPLODES_3X3,
1466     &yy_ei.explosion_type
1467   },
1468   {
1469     -1,                                 -1,
1470     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1471     &xx_ei.explosion_delay,             16,
1472     &yy_ei.explosion_delay
1473   },
1474   {
1475     -1,                                 -1,
1476     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1477     &xx_ei.ignition_delay,              8,
1478     &yy_ei.ignition_delay
1479   },
1480
1481   {
1482     -1,                                 -1,
1483     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1484     &xx_ei.content,                     EL_EMPTY_SPACE,
1485     &yy_ei.content,
1486     &xx_num_contents,                   1, 1
1487   },
1488
1489   // ---------- "num_change_pages" must be the last entry ---------------------
1490
1491   {
1492     -1,                                 SAVE_CONF_ALWAYS,
1493     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1494     &xx_ei.num_change_pages,            1,
1495     &yy_ei.num_change_pages
1496   },
1497
1498   {
1499     -1,                                 -1,
1500     -1,                                 -1,
1501     NULL,                               -1,
1502     NULL
1503   }
1504 };
1505
1506 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1507 {
1508   // ---------- "current_change_page" must be the first entry -----------------
1509
1510   {
1511     -1,                                 SAVE_CONF_ALWAYS,
1512     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1513     &xx_current_change_page,            -1
1514   },
1515
1516   // ---------- (the remaining entries can be in any order) -------------------
1517
1518   {
1519     -1,                                 -1,
1520     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1521     &xx_change.can_change,              FALSE
1522   },
1523
1524   {
1525     -1,                                 -1,
1526     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1527     &xx_event_bits[0],                  0
1528   },
1529   {
1530     -1,                                 -1,
1531     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1532     &xx_event_bits[1],                  0
1533   },
1534
1535   {
1536     -1,                                 -1,
1537     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1538     &xx_change.trigger_player,          CH_PLAYER_ANY
1539   },
1540   {
1541     -1,                                 -1,
1542     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1543     &xx_change.trigger_side,            CH_SIDE_ANY
1544   },
1545   {
1546     -1,                                 -1,
1547     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1548     &xx_change.trigger_page,            CH_PAGE_ANY
1549   },
1550
1551   {
1552     -1,                                 -1,
1553     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1554     &xx_change.target_element,          EL_EMPTY_SPACE
1555   },
1556
1557   {
1558     -1,                                 -1,
1559     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1560     &xx_change.delay_fixed,             0
1561   },
1562   {
1563     -1,                                 -1,
1564     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1565     &xx_change.delay_random,            0
1566   },
1567   {
1568     -1,                                 -1,
1569     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1570     &xx_change.delay_frames,            FRAMES_PER_SECOND
1571   },
1572
1573   {
1574     -1,                                 -1,
1575     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1576     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1577   },
1578
1579   {
1580     -1,                                 -1,
1581     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1582     &xx_change.explode,                 FALSE
1583   },
1584   {
1585     -1,                                 -1,
1586     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1587     &xx_change.use_target_content,      FALSE
1588   },
1589   {
1590     -1,                                 -1,
1591     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1592     &xx_change.only_if_complete,        FALSE
1593   },
1594   {
1595     -1,                                 -1,
1596     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1597     &xx_change.use_random_replace,      FALSE
1598   },
1599   {
1600     -1,                                 -1,
1601     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1602     &xx_change.random_percentage,       100
1603   },
1604   {
1605     -1,                                 -1,
1606     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1607     &xx_change.replace_when,            CP_WHEN_EMPTY
1608   },
1609
1610   {
1611     -1,                                 -1,
1612     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1613     &xx_change.has_action,              FALSE
1614   },
1615   {
1616     -1,                                 -1,
1617     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1618     &xx_change.action_type,             CA_NO_ACTION
1619   },
1620   {
1621     -1,                                 -1,
1622     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1623     &xx_change.action_mode,             CA_MODE_UNDEFINED
1624   },
1625   {
1626     -1,                                 -1,
1627     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1628     &xx_change.action_arg,              CA_ARG_UNDEFINED
1629   },
1630
1631   {
1632     -1,                                 -1,
1633     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1634     &xx_change.action_element,          EL_EMPTY_SPACE
1635   },
1636
1637   {
1638     -1,                                 -1,
1639     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1640     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1641     &xx_num_contents,                   1, 1
1642   },
1643
1644   {
1645     -1,                                 -1,
1646     -1,                                 -1,
1647     NULL,                               -1
1648   }
1649 };
1650
1651 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1652 {
1653   {
1654     -1,                                 -1,
1655     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1656     &xx_ei.description[0],              -1, NULL,
1657     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1658     &xx_default_description[0]
1659   },
1660
1661   {
1662     -1,                                 -1,
1663     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1664     &xx_ei.use_gfx_element,             FALSE
1665   },
1666   {
1667     -1,                                 -1,
1668     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1669     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1670   },
1671
1672   {
1673     -1,                                 -1,
1674     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1675     &xx_group.choice_mode,              ANIM_RANDOM
1676   },
1677
1678   {
1679     -1,                                 -1,
1680     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1681     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1682     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1683   },
1684
1685   {
1686     -1,                                 -1,
1687     -1,                                 -1,
1688     NULL,                               -1
1689   }
1690 };
1691
1692 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1693 {
1694   {
1695     -1,                                 -1,
1696     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1697     &xx_ei.use_gfx_element,             FALSE
1698   },
1699   {
1700     -1,                                 -1,
1701     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1702     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1703   },
1704
1705   {
1706     -1,                                 -1,
1707     -1,                                 -1,
1708     NULL,                               -1
1709   }
1710 };
1711
1712 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1713 {
1714   {
1715     EL_PLAYER_1,                        -1,
1716     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1717     &li.block_snap_field,               TRUE
1718   },
1719   {
1720     EL_PLAYER_1,                        -1,
1721     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1722     &li.continuous_snapping,            TRUE
1723   },
1724   {
1725     EL_PLAYER_1,                        -1,
1726     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1727     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1728   },
1729   {
1730     EL_PLAYER_1,                        -1,
1731     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1732     &li.use_start_element[0],           FALSE
1733   },
1734   {
1735     EL_PLAYER_1,                        -1,
1736     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1737     &li.start_element[0],               EL_PLAYER_1
1738   },
1739   {
1740     EL_PLAYER_1,                        -1,
1741     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1742     &li.use_artwork_element[0],         FALSE
1743   },
1744   {
1745     EL_PLAYER_1,                        -1,
1746     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1747     &li.artwork_element[0],             EL_PLAYER_1
1748   },
1749   {
1750     EL_PLAYER_1,                        -1,
1751     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1752     &li.use_explosion_element[0],       FALSE
1753   },
1754   {
1755     EL_PLAYER_1,                        -1,
1756     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1757     &li.explosion_element[0],           EL_PLAYER_1
1758   },
1759
1760   {
1761     -1,                                 -1,
1762     -1,                                 -1,
1763     NULL,                               -1
1764   }
1765 };
1766
1767 static struct
1768 {
1769   int filetype;
1770   char *id;
1771 }
1772 filetype_id_list[] =
1773 {
1774   { LEVEL_FILE_TYPE_RND,        "RND"   },
1775   { LEVEL_FILE_TYPE_BD,         "BD"    },
1776   { LEVEL_FILE_TYPE_EM,         "EM"    },
1777   { LEVEL_FILE_TYPE_SP,         "SP"    },
1778   { LEVEL_FILE_TYPE_DX,         "DX"    },
1779   { LEVEL_FILE_TYPE_SB,         "SB"    },
1780   { LEVEL_FILE_TYPE_DC,         "DC"    },
1781   { LEVEL_FILE_TYPE_MM,         "MM"    },
1782   { LEVEL_FILE_TYPE_MM,         "DF"    },
1783   { -1,                         NULL    },
1784 };
1785
1786
1787 // ============================================================================
1788 // level file functions
1789 // ============================================================================
1790
1791 static boolean check_special_flags(char *flag)
1792 {
1793   if (strEqual(options.special_flags, flag) ||
1794       strEqual(leveldir_current->special_flags, flag))
1795     return TRUE;
1796
1797   return FALSE;
1798 }
1799
1800 static struct DateInfo getCurrentDate(void)
1801 {
1802   time_t epoch_seconds = time(NULL);
1803   struct tm *now = localtime(&epoch_seconds);
1804   struct DateInfo date;
1805
1806   date.year  = now->tm_year + 1900;
1807   date.month = now->tm_mon  + 1;
1808   date.day   = now->tm_mday;
1809
1810   date.src   = DATE_SRC_CLOCK;
1811
1812   return date;
1813 }
1814
1815 static void resetEventFlags(struct ElementChangeInfo *change)
1816 {
1817   int i;
1818
1819   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1820     change->has_event[i] = FALSE;
1821 }
1822
1823 static void resetEventBits(void)
1824 {
1825   int i;
1826
1827   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1828     xx_event_bits[i] = 0;
1829 }
1830
1831 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1832 {
1833   int i;
1834
1835   /* important: only change event flag if corresponding event bit is set
1836      (this is because all xx_event_bits[] values are loaded separately,
1837      and all xx_event_bits[] values are set back to zero before loading
1838      another value xx_event_bits[x] (each value representing 32 flags)) */
1839
1840   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1841     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1842       change->has_event[i] = TRUE;
1843 }
1844
1845 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1846 {
1847   int i;
1848
1849   /* in contrast to the above function setEventFlagsFromEventBits(), it
1850      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1851      depending on the corresponding change->has_event[i] values here, as
1852      all xx_event_bits[] values are reset in resetEventBits() before */
1853
1854   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1855     if (change->has_event[i])
1856       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1857 }
1858
1859 static char *getDefaultElementDescription(struct ElementInfo *ei)
1860 {
1861   static char description[MAX_ELEMENT_NAME_LEN + 1];
1862   char *default_description = (ei->custom_description != NULL ?
1863                                ei->custom_description :
1864                                ei->editor_description);
1865   int i;
1866
1867   // always start with reliable default values
1868   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1869     description[i] = '\0';
1870
1871   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1872   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1873
1874   return &description[0];
1875 }
1876
1877 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1878 {
1879   char *default_description = getDefaultElementDescription(ei);
1880   int i;
1881
1882   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1883     ei->description[i] = default_description[i];
1884 }
1885
1886 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1887 {
1888   int i;
1889
1890   for (i = 0; conf[i].data_type != -1; i++)
1891   {
1892     int default_value = conf[i].default_value;
1893     int data_type = conf[i].data_type;
1894     int conf_type = conf[i].conf_type;
1895     int byte_mask = conf_type & CONF_MASK_BYTES;
1896
1897     if (byte_mask == CONF_MASK_MULTI_BYTES)
1898     {
1899       int default_num_entities = conf[i].default_num_entities;
1900       int max_num_entities = conf[i].max_num_entities;
1901
1902       *(int *)(conf[i].num_entities) = default_num_entities;
1903
1904       if (data_type == TYPE_STRING)
1905       {
1906         char *default_string = conf[i].default_string;
1907         char *string = (char *)(conf[i].value);
1908
1909         strncpy(string, default_string, max_num_entities);
1910       }
1911       else if (data_type == TYPE_ELEMENT_LIST)
1912       {
1913         int *element_array = (int *)(conf[i].value);
1914         int j;
1915
1916         for (j = 0; j < max_num_entities; j++)
1917           element_array[j] = default_value;
1918       }
1919       else if (data_type == TYPE_CONTENT_LIST)
1920       {
1921         struct Content *content = (struct Content *)(conf[i].value);
1922         int c, x, y;
1923
1924         for (c = 0; c < max_num_entities; c++)
1925           for (y = 0; y < 3; y++)
1926             for (x = 0; x < 3; x++)
1927               content[c].e[x][y] = default_value;
1928       }
1929     }
1930     else        // constant size configuration data (1, 2 or 4 bytes)
1931     {
1932       if (data_type == TYPE_BOOLEAN)
1933         *(boolean *)(conf[i].value) = default_value;
1934       else
1935         *(int *)    (conf[i].value) = default_value;
1936     }
1937   }
1938 }
1939
1940 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1941 {
1942   int i;
1943
1944   for (i = 0; conf[i].data_type != -1; i++)
1945   {
1946     int data_type = conf[i].data_type;
1947     int conf_type = conf[i].conf_type;
1948     int byte_mask = conf_type & CONF_MASK_BYTES;
1949
1950     if (byte_mask == CONF_MASK_MULTI_BYTES)
1951     {
1952       int max_num_entities = conf[i].max_num_entities;
1953
1954       if (data_type == TYPE_STRING)
1955       {
1956         char *string      = (char *)(conf[i].value);
1957         char *string_copy = (char *)(conf[i].value_copy);
1958
1959         strncpy(string_copy, string, max_num_entities);
1960       }
1961       else if (data_type == TYPE_ELEMENT_LIST)
1962       {
1963         int *element_array      = (int *)(conf[i].value);
1964         int *element_array_copy = (int *)(conf[i].value_copy);
1965         int j;
1966
1967         for (j = 0; j < max_num_entities; j++)
1968           element_array_copy[j] = element_array[j];
1969       }
1970       else if (data_type == TYPE_CONTENT_LIST)
1971       {
1972         struct Content *content      = (struct Content *)(conf[i].value);
1973         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1974         int c, x, y;
1975
1976         for (c = 0; c < max_num_entities; c++)
1977           for (y = 0; y < 3; y++)
1978             for (x = 0; x < 3; x++)
1979               content_copy[c].e[x][y] = content[c].e[x][y];
1980       }
1981     }
1982     else        // constant size configuration data (1, 2 or 4 bytes)
1983     {
1984       if (data_type == TYPE_BOOLEAN)
1985         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1986       else
1987         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
1988     }
1989   }
1990 }
1991
1992 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1993 {
1994   int i;
1995
1996   xx_ei = *ei_from;     // copy element data into temporary buffer
1997   yy_ei = *ei_to;       // copy element data into temporary buffer
1998
1999   copyConfigFromConfigList(chunk_config_CUSX_base);
2000
2001   *ei_from = xx_ei;
2002   *ei_to   = yy_ei;
2003
2004   // ---------- reinitialize and copy change pages ----------
2005
2006   ei_to->num_change_pages = ei_from->num_change_pages;
2007   ei_to->current_change_page = ei_from->current_change_page;
2008
2009   setElementChangePages(ei_to, ei_to->num_change_pages);
2010
2011   for (i = 0; i < ei_to->num_change_pages; i++)
2012     ei_to->change_page[i] = ei_from->change_page[i];
2013
2014   // ---------- copy group element info ----------
2015   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2016     *ei_to->group = *ei_from->group;
2017
2018   // mark this custom element as modified
2019   ei_to->modified_settings = TRUE;
2020 }
2021
2022 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2023 {
2024   int change_page_size = sizeof(struct ElementChangeInfo);
2025
2026   ei->num_change_pages = MAX(1, change_pages);
2027
2028   ei->change_page =
2029     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2030
2031   if (ei->current_change_page >= ei->num_change_pages)
2032     ei->current_change_page = ei->num_change_pages - 1;
2033
2034   ei->change = &ei->change_page[ei->current_change_page];
2035 }
2036
2037 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2038 {
2039   xx_change = *change;          // copy change data into temporary buffer
2040
2041   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2042
2043   *change = xx_change;
2044
2045   resetEventFlags(change);
2046
2047   change->direct_action = 0;
2048   change->other_action = 0;
2049
2050   change->pre_change_function = NULL;
2051   change->change_function = NULL;
2052   change->post_change_function = NULL;
2053 }
2054
2055 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2056 {
2057   int i, x, y;
2058
2059   li = *level;          // copy level data into temporary buffer
2060   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2061   *level = li;          // copy temporary buffer back to level data
2062
2063   setLevelInfoToDefaults_BD();
2064   setLevelInfoToDefaults_EM();
2065   setLevelInfoToDefaults_SP();
2066   setLevelInfoToDefaults_MM();
2067
2068   level->native_bd_level = &native_bd_level;
2069   level->native_em_level = &native_em_level;
2070   level->native_sp_level = &native_sp_level;
2071   level->native_mm_level = &native_mm_level;
2072
2073   level->file_version = FILE_VERSION_ACTUAL;
2074   level->game_version = GAME_VERSION_ACTUAL;
2075
2076   level->creation_date = getCurrentDate();
2077
2078   level->encoding_16bit_field  = TRUE;
2079   level->encoding_16bit_yamyam = TRUE;
2080   level->encoding_16bit_amoeba = TRUE;
2081
2082   // clear level name and level author string buffers
2083   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2084     level->name[i] = '\0';
2085   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2086     level->author[i] = '\0';
2087
2088   // set level name and level author to default values
2089   strcpy(level->name, NAMELESS_LEVEL_NAME);
2090   strcpy(level->author, ANONYMOUS_NAME);
2091
2092   // set level playfield to playable default level with player and exit
2093   for (x = 0; x < MAX_LEV_FIELDX; x++)
2094     for (y = 0; y < MAX_LEV_FIELDY; y++)
2095       level->field[x][y] = EL_SAND;
2096
2097   level->field[0][0] = EL_PLAYER_1;
2098   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2099
2100   BorderElement = EL_STEELWALL;
2101
2102   // detect custom elements when loading them
2103   level->file_has_custom_elements = FALSE;
2104
2105   // set all bug compatibility flags to "false" => do not emulate this bug
2106   level->use_action_after_change_bug = FALSE;
2107
2108   if (leveldir_current)
2109   {
2110     // try to determine better author name than 'anonymous'
2111     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2112     {
2113       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2114       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2115     }
2116     else
2117     {
2118       switch (LEVELCLASS(leveldir_current))
2119       {
2120         case LEVELCLASS_TUTORIAL:
2121           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2122           break;
2123
2124         case LEVELCLASS_CONTRIB:
2125           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2126           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2127           break;
2128
2129         case LEVELCLASS_PRIVATE:
2130           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2131           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2132           break;
2133
2134         default:
2135           // keep default value
2136           break;
2137       }
2138     }
2139   }
2140 }
2141
2142 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2143 {
2144   static boolean clipboard_elements_initialized = FALSE;
2145   int i;
2146
2147   InitElementPropertiesStatic();
2148
2149   li = *level;          // copy level data into temporary buffer
2150   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2151   *level = li;          // copy temporary buffer back to level data
2152
2153   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2154   {
2155     int element = i;
2156     struct ElementInfo *ei = &element_info[element];
2157
2158     if (element == EL_MM_GRAY_BALL)
2159     {
2160       struct LevelInfo_MM *level_mm = level->native_mm_level;
2161       int j;
2162
2163       for (j = 0; j < level->num_mm_ball_contents; j++)
2164         level->mm_ball_content[j] =
2165           map_element_MM_to_RND(level_mm->ball_content[j]);
2166     }
2167
2168     // never initialize clipboard elements after the very first time
2169     // (to be able to use clipboard elements between several levels)
2170     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2171       continue;
2172
2173     if (IS_ENVELOPE(element))
2174     {
2175       int envelope_nr = element - EL_ENVELOPE_1;
2176
2177       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2178
2179       level->envelope[envelope_nr] = xx_envelope;
2180     }
2181
2182     if (IS_CUSTOM_ELEMENT(element) ||
2183         IS_GROUP_ELEMENT(element) ||
2184         IS_INTERNAL_ELEMENT(element))
2185     {
2186       xx_ei = *ei;      // copy element data into temporary buffer
2187
2188       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2189
2190       *ei = xx_ei;
2191     }
2192
2193     setElementChangePages(ei, 1);
2194     setElementChangeInfoToDefaults(ei->change);
2195
2196     if (IS_CUSTOM_ELEMENT(element) ||
2197         IS_GROUP_ELEMENT(element))
2198     {
2199       setElementDescriptionToDefault(ei);
2200
2201       ei->modified_settings = FALSE;
2202     }
2203
2204     if (IS_CUSTOM_ELEMENT(element) ||
2205         IS_INTERNAL_ELEMENT(element))
2206     {
2207       // internal values used in level editor
2208
2209       ei->access_type = 0;
2210       ei->access_layer = 0;
2211       ei->access_protected = 0;
2212       ei->walk_to_action = 0;
2213       ei->smash_targets = 0;
2214       ei->deadliness = 0;
2215
2216       ei->can_explode_by_fire = FALSE;
2217       ei->can_explode_smashed = FALSE;
2218       ei->can_explode_impact = FALSE;
2219
2220       ei->current_change_page = 0;
2221     }
2222
2223     if (IS_GROUP_ELEMENT(element) ||
2224         IS_INTERNAL_ELEMENT(element))
2225     {
2226       struct ElementGroupInfo *group;
2227
2228       // initialize memory for list of elements in group
2229       if (ei->group == NULL)
2230         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2231
2232       group = ei->group;
2233
2234       xx_group = *group;        // copy group data into temporary buffer
2235
2236       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2237
2238       *group = xx_group;
2239     }
2240
2241     if (IS_EMPTY_ELEMENT(element) ||
2242         IS_INTERNAL_ELEMENT(element))
2243     {
2244       xx_ei = *ei;              // copy element data into temporary buffer
2245
2246       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2247
2248       *ei = xx_ei;
2249     }
2250   }
2251
2252   clipboard_elements_initialized = TRUE;
2253 }
2254
2255 static void setLevelInfoToDefaults(struct LevelInfo *level,
2256                                    boolean level_info_only,
2257                                    boolean reset_file_status)
2258 {
2259   setLevelInfoToDefaults_Level(level);
2260
2261   if (!level_info_only)
2262     setLevelInfoToDefaults_Elements(level);
2263
2264   if (reset_file_status)
2265   {
2266     level->no_valid_file = FALSE;
2267     level->no_level_file = FALSE;
2268   }
2269
2270   level->changed = FALSE;
2271 }
2272
2273 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2274 {
2275   level_file_info->nr = 0;
2276   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2277   level_file_info->packed = FALSE;
2278
2279   setString(&level_file_info->basename, NULL);
2280   setString(&level_file_info->filename, NULL);
2281 }
2282
2283 int getMappedElement_SB(int, boolean);
2284
2285 static void ActivateLevelTemplate(void)
2286 {
2287   int x, y;
2288
2289   if (check_special_flags("load_xsb_to_ces"))
2290   {
2291     // fill smaller playfields with padding "beyond border wall" elements
2292     if (level.fieldx < level_template.fieldx ||
2293         level.fieldy < level_template.fieldy)
2294     {
2295       short field[level.fieldx][level.fieldy];
2296       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2297       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2298       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2299       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2300
2301       // copy old playfield (which is smaller than the visible area)
2302       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2303         field[x][y] = level.field[x][y];
2304
2305       // fill new, larger playfield with "beyond border wall" elements
2306       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2307         level.field[x][y] = getMappedElement_SB('_', TRUE);
2308
2309       // copy the old playfield to the middle of the new playfield
2310       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2311         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2312
2313       level.fieldx = new_fieldx;
2314       level.fieldy = new_fieldy;
2315     }
2316   }
2317
2318   // Currently there is no special action needed to activate the template
2319   // data, because 'element_info' property settings overwrite the original
2320   // level data, while all other variables do not change.
2321
2322   // Exception: 'from_level_template' elements in the original level playfield
2323   // are overwritten with the corresponding elements at the same position in
2324   // playfield from the level template.
2325
2326   for (x = 0; x < level.fieldx; x++)
2327     for (y = 0; y < level.fieldy; y++)
2328       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2329         level.field[x][y] = level_template.field[x][y];
2330
2331   if (check_special_flags("load_xsb_to_ces"))
2332   {
2333     struct LevelInfo level_backup = level;
2334
2335     // overwrite all individual level settings from template level settings
2336     level = level_template;
2337
2338     // restore level file info
2339     level.file_info = level_backup.file_info;
2340
2341     // restore playfield size
2342     level.fieldx = level_backup.fieldx;
2343     level.fieldy = level_backup.fieldy;
2344
2345     // restore playfield content
2346     for (x = 0; x < level.fieldx; x++)
2347       for (y = 0; y < level.fieldy; y++)
2348         level.field[x][y] = level_backup.field[x][y];
2349
2350     // restore name and author from individual level
2351     strcpy(level.name,   level_backup.name);
2352     strcpy(level.author, level_backup.author);
2353
2354     // restore flag "use_custom_template"
2355     level.use_custom_template = level_backup.use_custom_template;
2356   }
2357 }
2358
2359 static boolean checkForPackageFromBasename_BD(char *basename)
2360 {
2361   // check for native BD level file extensions
2362   if (!strSuffixLower(basename, ".bd") &&
2363       !strSuffixLower(basename, ".bdr") &&
2364       !strSuffixLower(basename, ".brc") &&
2365       !strSuffixLower(basename, ".gds"))
2366     return FALSE;
2367
2368   // check for standard single-level BD files (like "001.bd")
2369   if (strSuffixLower(basename, ".bd") &&
2370       strlen(basename) == 6 &&
2371       basename[0] >= '0' && basename[0] <= '9' &&
2372       basename[1] >= '0' && basename[1] <= '9' &&
2373       basename[2] >= '0' && basename[2] <= '9')
2374     return FALSE;
2375
2376   // this is a level package in native BD file format
2377   return TRUE;
2378 }
2379
2380 static char *getLevelFilenameFromBasename(char *basename)
2381 {
2382   static char *filename = NULL;
2383
2384   checked_free(filename);
2385
2386   filename = getPath2(getCurrentLevelDir(), basename);
2387
2388   return filename;
2389 }
2390
2391 static int getFileTypeFromBasename(char *basename)
2392 {
2393   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2394
2395   static char *filename = NULL;
2396   struct stat file_status;
2397
2398   // ---------- try to determine file type from filename ----------
2399
2400   // check for typical filename of a Supaplex level package file
2401   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2402     return LEVEL_FILE_TYPE_SP;
2403
2404   // check for typical filename of a Diamond Caves II level package file
2405   if (strSuffixLower(basename, ".dc") ||
2406       strSuffixLower(basename, ".dc2"))
2407     return LEVEL_FILE_TYPE_DC;
2408
2409   // check for typical filename of a Sokoban level package file
2410   if (strSuffixLower(basename, ".xsb") &&
2411       strchr(basename, '%') == NULL)
2412     return LEVEL_FILE_TYPE_SB;
2413
2414   // check for typical filename of a Boulder Dash (GDash) level package file
2415   if (checkForPackageFromBasename_BD(basename))
2416     return LEVEL_FILE_TYPE_BD;
2417
2418   // ---------- try to determine file type from filesize ----------
2419
2420   checked_free(filename);
2421   filename = getPath2(getCurrentLevelDir(), basename);
2422
2423   if (stat(filename, &file_status) == 0)
2424   {
2425     // check for typical filesize of a Supaplex level package file
2426     if (file_status.st_size == 170496)
2427       return LEVEL_FILE_TYPE_SP;
2428   }
2429
2430   return LEVEL_FILE_TYPE_UNKNOWN;
2431 }
2432
2433 static int getFileTypeFromMagicBytes(char *filename, int type)
2434 {
2435   File *file;
2436
2437   if ((file = openFile(filename, MODE_READ)))
2438   {
2439     char chunk_name[CHUNK_ID_LEN + 1];
2440
2441     getFileChunkBE(file, chunk_name, NULL);
2442
2443     if (strEqual(chunk_name, "MMII") ||
2444         strEqual(chunk_name, "MIRR"))
2445       type = LEVEL_FILE_TYPE_MM;
2446
2447     closeFile(file);
2448   }
2449
2450   return type;
2451 }
2452
2453 static boolean checkForPackageFromBasename(char *basename)
2454 {
2455   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2456   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2457
2458   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2459 }
2460
2461 static char *getSingleLevelBasenameExt(int nr, char *extension)
2462 {
2463   static char basename[MAX_FILENAME_LEN];
2464
2465   if (nr < 0)
2466     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2467   else
2468     sprintf(basename, "%03d.%s", nr, extension);
2469
2470   return basename;
2471 }
2472
2473 static char *getSingleLevelBasename(int nr)
2474 {
2475   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2476 }
2477
2478 static char *getPackedLevelBasename(int type)
2479 {
2480   static char basename[MAX_FILENAME_LEN];
2481   char *directory = getCurrentLevelDir();
2482   Directory *dir;
2483   DirectoryEntry *dir_entry;
2484
2485   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2486
2487   if ((dir = openDirectory(directory)) == NULL)
2488   {
2489     Warn("cannot read current level directory '%s'", directory);
2490
2491     return basename;
2492   }
2493
2494   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2495   {
2496     char *entry_basename = dir_entry->basename;
2497     int entry_type = getFileTypeFromBasename(entry_basename);
2498
2499     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2500     {
2501       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2502           type == entry_type)
2503       {
2504         strcpy(basename, entry_basename);
2505
2506         break;
2507       }
2508     }
2509   }
2510
2511   closeDirectory(dir);
2512
2513   return basename;
2514 }
2515
2516 static char *getSingleLevelFilename(int nr)
2517 {
2518   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2519 }
2520
2521 #if ENABLE_UNUSED_CODE
2522 static char *getPackedLevelFilename(int type)
2523 {
2524   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2525 }
2526 #endif
2527
2528 char *getDefaultLevelFilename(int nr)
2529 {
2530   return getSingleLevelFilename(nr);
2531 }
2532
2533 #if ENABLE_UNUSED_CODE
2534 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2535                                                  int type)
2536 {
2537   lfi->type = type;
2538   lfi->packed = FALSE;
2539
2540   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2541   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2542 }
2543 #endif
2544
2545 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2546                                                  int type, char *format, ...)
2547 {
2548   static char basename[MAX_FILENAME_LEN];
2549   va_list ap;
2550
2551   va_start(ap, format);
2552   vsprintf(basename, format, ap);
2553   va_end(ap);
2554
2555   lfi->type = type;
2556   lfi->packed = FALSE;
2557
2558   setString(&lfi->basename, basename);
2559   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2560 }
2561
2562 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2563                                                  int type)
2564 {
2565   lfi->type = type;
2566   lfi->packed = TRUE;
2567
2568   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2569   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2570 }
2571
2572 static int getFiletypeFromID(char *filetype_id)
2573 {
2574   char *filetype_id_lower;
2575   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2576   int i;
2577
2578   if (filetype_id == NULL)
2579     return LEVEL_FILE_TYPE_UNKNOWN;
2580
2581   filetype_id_lower = getStringToLower(filetype_id);
2582
2583   for (i = 0; filetype_id_list[i].id != NULL; i++)
2584   {
2585     char *id_lower = getStringToLower(filetype_id_list[i].id);
2586     
2587     if (strEqual(filetype_id_lower, id_lower))
2588       filetype = filetype_id_list[i].filetype;
2589
2590     free(id_lower);
2591
2592     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2593       break;
2594   }
2595
2596   free(filetype_id_lower);
2597
2598   return filetype;
2599 }
2600
2601 char *getLocalLevelTemplateFilename(void)
2602 {
2603   return getDefaultLevelFilename(-1);
2604 }
2605
2606 char *getGlobalLevelTemplateFilename(void)
2607 {
2608   // global variable "leveldir_current" must be modified in the loop below
2609   LevelDirTree *leveldir_current_last = leveldir_current;
2610   char *filename = NULL;
2611
2612   // check for template level in path from current to topmost tree node
2613
2614   while (leveldir_current != NULL)
2615   {
2616     filename = getDefaultLevelFilename(-1);
2617
2618     if (fileExists(filename))
2619       break;
2620
2621     leveldir_current = leveldir_current->node_parent;
2622   }
2623
2624   // restore global variable "leveldir_current" modified in above loop
2625   leveldir_current = leveldir_current_last;
2626
2627   return filename;
2628 }
2629
2630 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2631 {
2632   int nr = lfi->nr;
2633
2634   // special case: level number is negative => check for level template file
2635   if (nr < 0)
2636   {
2637     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2638                                          getSingleLevelBasename(-1));
2639
2640     // replace local level template filename with global template filename
2641     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2642
2643     // no fallback if template file not existing
2644     return;
2645   }
2646
2647   // special case: check for file name/pattern specified in "levelinfo.conf"
2648   if (leveldir_current->level_filename != NULL)
2649   {
2650     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2651
2652     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2653                                          leveldir_current->level_filename, nr);
2654
2655     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2656
2657     if (fileExists(lfi->filename))
2658       return;
2659   }
2660   else if (leveldir_current->level_filetype != NULL)
2661   {
2662     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2663
2664     // check for specified native level file with standard file name
2665     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2666                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2667     if (fileExists(lfi->filename))
2668       return;
2669   }
2670
2671   // check for native Rocks'n'Diamonds level file
2672   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2673                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2674   if (fileExists(lfi->filename))
2675     return;
2676
2677   // check for native Boulder Dash level file
2678   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2679   if (fileExists(lfi->filename))
2680     return;
2681
2682   // check for Emerald Mine level file (V1)
2683   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2684                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2685   if (fileExists(lfi->filename))
2686     return;
2687   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2688                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2689   if (fileExists(lfi->filename))
2690     return;
2691
2692   // check for Emerald Mine level file (V2 to V5)
2693   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2694   if (fileExists(lfi->filename))
2695     return;
2696
2697   // check for Emerald Mine level file (V6 / single mode)
2698   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2699   if (fileExists(lfi->filename))
2700     return;
2701   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2702   if (fileExists(lfi->filename))
2703     return;
2704
2705   // check for Emerald Mine level file (V6 / teamwork mode)
2706   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2707   if (fileExists(lfi->filename))
2708     return;
2709   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2710   if (fileExists(lfi->filename))
2711     return;
2712
2713   // check for various packed level file formats
2714   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2715   if (fileExists(lfi->filename))
2716     return;
2717
2718   // no known level file found -- use default values (and fail later)
2719   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2720                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2721 }
2722
2723 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2724 {
2725   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2726     lfi->type = getFileTypeFromBasename(lfi->basename);
2727
2728   if (lfi->type == LEVEL_FILE_TYPE_RND)
2729     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2730 }
2731
2732 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2733 {
2734   // always start with reliable default values
2735   setFileInfoToDefaults(level_file_info);
2736
2737   level_file_info->nr = nr;     // set requested level number
2738
2739   determineLevelFileInfo_Filename(level_file_info);
2740   determineLevelFileInfo_Filetype(level_file_info);
2741 }
2742
2743 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2744                               struct LevelFileInfo *lfi_to)
2745 {
2746   lfi_to->nr     = lfi_from->nr;
2747   lfi_to->type   = lfi_from->type;
2748   lfi_to->packed = lfi_from->packed;
2749
2750   setString(&lfi_to->basename, lfi_from->basename);
2751   setString(&lfi_to->filename, lfi_from->filename);
2752 }
2753
2754 // ----------------------------------------------------------------------------
2755 // functions for loading R'n'D level
2756 // ----------------------------------------------------------------------------
2757
2758 int getMappedElement(int element)
2759 {
2760   // remap some (historic, now obsolete) elements
2761
2762   switch (element)
2763   {
2764     case EL_PLAYER_OBSOLETE:
2765       element = EL_PLAYER_1;
2766       break;
2767
2768     case EL_KEY_OBSOLETE:
2769       element = EL_KEY_1;
2770       break;
2771
2772     case EL_EM_KEY_1_FILE_OBSOLETE:
2773       element = EL_EM_KEY_1;
2774       break;
2775
2776     case EL_EM_KEY_2_FILE_OBSOLETE:
2777       element = EL_EM_KEY_2;
2778       break;
2779
2780     case EL_EM_KEY_3_FILE_OBSOLETE:
2781       element = EL_EM_KEY_3;
2782       break;
2783
2784     case EL_EM_KEY_4_FILE_OBSOLETE:
2785       element = EL_EM_KEY_4;
2786       break;
2787
2788     case EL_ENVELOPE_OBSOLETE:
2789       element = EL_ENVELOPE_1;
2790       break;
2791
2792     case EL_SP_EMPTY:
2793       element = EL_EMPTY;
2794       break;
2795
2796     default:
2797       if (element >= NUM_FILE_ELEMENTS)
2798       {
2799         Warn("invalid level element %d", element);
2800
2801         element = EL_UNKNOWN;
2802       }
2803       break;
2804   }
2805
2806   return element;
2807 }
2808
2809 static int getMappedElementByVersion(int element, int game_version)
2810 {
2811   // remap some elements due to certain game version
2812
2813   if (game_version <= VERSION_IDENT(2,2,0,0))
2814   {
2815     // map game font elements
2816     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2817                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2818                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2819                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2820   }
2821
2822   if (game_version < VERSION_IDENT(3,0,0,0))
2823   {
2824     // map Supaplex gravity tube elements
2825     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2826                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2827                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2828                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2829                element);
2830   }
2831
2832   return element;
2833 }
2834
2835 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2836 {
2837   level->file_version = getFileVersion(file);
2838   level->game_version = getFileVersion(file);
2839
2840   return chunk_size;
2841 }
2842
2843 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2844 {
2845   level->creation_date.year  = getFile16BitBE(file);
2846   level->creation_date.month = getFile8Bit(file);
2847   level->creation_date.day   = getFile8Bit(file);
2848
2849   level->creation_date.src   = DATE_SRC_LEVELFILE;
2850
2851   return chunk_size;
2852 }
2853
2854 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2855 {
2856   int initial_player_stepsize;
2857   int initial_player_gravity;
2858   int i, x, y;
2859
2860   level->fieldx = getFile8Bit(file);
2861   level->fieldy = getFile8Bit(file);
2862
2863   level->time           = getFile16BitBE(file);
2864   level->gems_needed    = getFile16BitBE(file);
2865
2866   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2867     level->name[i] = getFile8Bit(file);
2868   level->name[MAX_LEVEL_NAME_LEN] = 0;
2869
2870   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2871     level->score[i] = getFile8Bit(file);
2872
2873   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2874   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2875     for (y = 0; y < 3; y++)
2876       for (x = 0; x < 3; x++)
2877         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2878
2879   level->amoeba_speed           = getFile8Bit(file);
2880   level->time_magic_wall        = getFile8Bit(file);
2881   level->time_wheel             = getFile8Bit(file);
2882   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2883
2884   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2885                                    STEPSIZE_NORMAL);
2886
2887   for (i = 0; i < MAX_PLAYERS; i++)
2888     level->initial_player_stepsize[i] = initial_player_stepsize;
2889
2890   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2891
2892   for (i = 0; i < MAX_PLAYERS; i++)
2893     level->initial_player_gravity[i] = initial_player_gravity;
2894
2895   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2896   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2897
2898   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2899
2900   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2901   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2902   level->can_move_into_acid_bits = getFile32BitBE(file);
2903   level->dont_collide_with_bits = getFile8Bit(file);
2904
2905   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2906   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2907
2908   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2909   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2910   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2911
2912   level->game_engine_type       = getFile8Bit(file);
2913
2914   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2915
2916   return chunk_size;
2917 }
2918
2919 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2920 {
2921   int i;
2922
2923   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2924     level->name[i] = getFile8Bit(file);
2925   level->name[MAX_LEVEL_NAME_LEN] = 0;
2926
2927   return chunk_size;
2928 }
2929
2930 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2931 {
2932   int i;
2933
2934   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2935     level->author[i] = getFile8Bit(file);
2936   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2937
2938   return chunk_size;
2939 }
2940
2941 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2942 {
2943   int x, y;
2944   int chunk_size_expected = level->fieldx * level->fieldy;
2945
2946   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2947      stored with 16-bit encoding (and should be twice as big then).
2948      Even worse, playfield data was stored 16-bit when only yamyam content
2949      contained 16-bit elements and vice versa. */
2950
2951   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2952     chunk_size_expected *= 2;
2953
2954   if (chunk_size_expected != chunk_size)
2955   {
2956     ReadUnusedBytesFromFile(file, chunk_size);
2957     return chunk_size_expected;
2958   }
2959
2960   for (y = 0; y < level->fieldy; y++)
2961     for (x = 0; x < level->fieldx; x++)
2962       level->field[x][y] =
2963         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2964                          getFile8Bit(file));
2965   return chunk_size;
2966 }
2967
2968 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2969 {
2970   int i, x, y;
2971   int header_size = 4;
2972   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2973   int chunk_size_expected = header_size + content_size;
2974
2975   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2976      stored with 16-bit encoding (and should be twice as big then).
2977      Even worse, playfield data was stored 16-bit when only yamyam content
2978      contained 16-bit elements and vice versa. */
2979
2980   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2981     chunk_size_expected += content_size;
2982
2983   if (chunk_size_expected != chunk_size)
2984   {
2985     ReadUnusedBytesFromFile(file, chunk_size);
2986     return chunk_size_expected;
2987   }
2988
2989   getFile8Bit(file);
2990   level->num_yamyam_contents = getFile8Bit(file);
2991   getFile8Bit(file);
2992   getFile8Bit(file);
2993
2994   // correct invalid number of content fields -- should never happen
2995   if (level->num_yamyam_contents < 1 ||
2996       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2997     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2998
2999   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3000     for (y = 0; y < 3; y++)
3001       for (x = 0; x < 3; x++)
3002         level->yamyam_content[i].e[x][y] =
3003           getMappedElement(level->encoding_16bit_field ?
3004                            getFile16BitBE(file) : getFile8Bit(file));
3005   return chunk_size;
3006 }
3007
3008 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3009 {
3010   int i, x, y;
3011   int element;
3012   int num_contents;
3013   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3014
3015   element = getMappedElement(getFile16BitBE(file));
3016   num_contents = getFile8Bit(file);
3017
3018   getFile8Bit(file);    // content x size (unused)
3019   getFile8Bit(file);    // content y size (unused)
3020
3021   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3022
3023   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3024     for (y = 0; y < 3; y++)
3025       for (x = 0; x < 3; x++)
3026         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3027
3028   // correct invalid number of content fields -- should never happen
3029   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3030     num_contents = STD_ELEMENT_CONTENTS;
3031
3032   if (element == EL_YAMYAM)
3033   {
3034     level->num_yamyam_contents = num_contents;
3035
3036     for (i = 0; i < num_contents; i++)
3037       for (y = 0; y < 3; y++)
3038         for (x = 0; x < 3; x++)
3039           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3040   }
3041   else if (element == EL_BD_AMOEBA)
3042   {
3043     level->amoeba_content = content_array[0][0][0];
3044   }
3045   else
3046   {
3047     Warn("cannot load content for element '%d'", element);
3048   }
3049
3050   return chunk_size;
3051 }
3052
3053 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3054 {
3055   int i;
3056   int element;
3057   int envelope_nr;
3058   int envelope_len;
3059   int chunk_size_expected;
3060
3061   element = getMappedElement(getFile16BitBE(file));
3062   if (!IS_ENVELOPE(element))
3063     element = EL_ENVELOPE_1;
3064
3065   envelope_nr = element - EL_ENVELOPE_1;
3066
3067   envelope_len = getFile16BitBE(file);
3068
3069   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3070   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3071
3072   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3073
3074   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3075   if (chunk_size_expected != chunk_size)
3076   {
3077     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3078     return chunk_size_expected;
3079   }
3080
3081   for (i = 0; i < envelope_len; i++)
3082     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3083
3084   return chunk_size;
3085 }
3086
3087 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3088 {
3089   int num_changed_custom_elements = getFile16BitBE(file);
3090   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3091   int i;
3092
3093   if (chunk_size_expected != chunk_size)
3094   {
3095     ReadUnusedBytesFromFile(file, chunk_size - 2);
3096     return chunk_size_expected;
3097   }
3098
3099   for (i = 0; i < num_changed_custom_elements; i++)
3100   {
3101     int element = getMappedElement(getFile16BitBE(file));
3102     int properties = getFile32BitBE(file);
3103
3104     if (IS_CUSTOM_ELEMENT(element))
3105       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3106     else
3107       Warn("invalid custom element number %d", element);
3108
3109     // older game versions that wrote level files with CUS1 chunks used
3110     // different default push delay values (not yet stored in level file)
3111     element_info[element].push_delay_fixed = 2;
3112     element_info[element].push_delay_random = 8;
3113   }
3114
3115   level->file_has_custom_elements = TRUE;
3116
3117   return chunk_size;
3118 }
3119
3120 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3121 {
3122   int num_changed_custom_elements = getFile16BitBE(file);
3123   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3124   int i;
3125
3126   if (chunk_size_expected != chunk_size)
3127   {
3128     ReadUnusedBytesFromFile(file, chunk_size - 2);
3129     return chunk_size_expected;
3130   }
3131
3132   for (i = 0; i < num_changed_custom_elements; i++)
3133   {
3134     int element = getMappedElement(getFile16BitBE(file));
3135     int custom_target_element = getMappedElement(getFile16BitBE(file));
3136
3137     if (IS_CUSTOM_ELEMENT(element))
3138       element_info[element].change->target_element = custom_target_element;
3139     else
3140       Warn("invalid custom element number %d", element);
3141   }
3142
3143   level->file_has_custom_elements = TRUE;
3144
3145   return chunk_size;
3146 }
3147
3148 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3149 {
3150   int num_changed_custom_elements = getFile16BitBE(file);
3151   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3152   int i, j, x, y;
3153
3154   if (chunk_size_expected != chunk_size)
3155   {
3156     ReadUnusedBytesFromFile(file, chunk_size - 2);
3157     return chunk_size_expected;
3158   }
3159
3160   for (i = 0; i < num_changed_custom_elements; i++)
3161   {
3162     int element = getMappedElement(getFile16BitBE(file));
3163     struct ElementInfo *ei = &element_info[element];
3164     unsigned int event_bits;
3165
3166     if (!IS_CUSTOM_ELEMENT(element))
3167     {
3168       Warn("invalid custom element number %d", element);
3169
3170       element = EL_INTERNAL_DUMMY;
3171     }
3172
3173     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3174       ei->description[j] = getFile8Bit(file);
3175     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3176
3177     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3178
3179     // some free bytes for future properties and padding
3180     ReadUnusedBytesFromFile(file, 7);
3181
3182     ei->use_gfx_element = getFile8Bit(file);
3183     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3184
3185     ei->collect_score_initial = getFile8Bit(file);
3186     ei->collect_count_initial = getFile8Bit(file);
3187
3188     ei->push_delay_fixed = getFile16BitBE(file);
3189     ei->push_delay_random = getFile16BitBE(file);
3190     ei->move_delay_fixed = getFile16BitBE(file);
3191     ei->move_delay_random = getFile16BitBE(file);
3192
3193     ei->move_pattern = getFile16BitBE(file);
3194     ei->move_direction_initial = getFile8Bit(file);
3195     ei->move_stepsize = getFile8Bit(file);
3196
3197     for (y = 0; y < 3; y++)
3198       for (x = 0; x < 3; x++)
3199         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3200
3201     // bits 0 - 31 of "has_event[]"
3202     event_bits = getFile32BitBE(file);
3203     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3204       if (event_bits & (1u << j))
3205         ei->change->has_event[j] = TRUE;
3206
3207     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3208
3209     ei->change->delay_fixed = getFile16BitBE(file);
3210     ei->change->delay_random = getFile16BitBE(file);
3211     ei->change->delay_frames = getFile16BitBE(file);
3212
3213     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3214
3215     ei->change->explode = getFile8Bit(file);
3216     ei->change->use_target_content = getFile8Bit(file);
3217     ei->change->only_if_complete = getFile8Bit(file);
3218     ei->change->use_random_replace = getFile8Bit(file);
3219
3220     ei->change->random_percentage = getFile8Bit(file);
3221     ei->change->replace_when = getFile8Bit(file);
3222
3223     for (y = 0; y < 3; y++)
3224       for (x = 0; x < 3; x++)
3225         ei->change->target_content.e[x][y] =
3226           getMappedElement(getFile16BitBE(file));
3227
3228     ei->slippery_type = getFile8Bit(file);
3229
3230     // some free bytes for future properties and padding
3231     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3232
3233     // mark that this custom element has been modified
3234     ei->modified_settings = TRUE;
3235   }
3236
3237   level->file_has_custom_elements = TRUE;
3238
3239   return chunk_size;
3240 }
3241
3242 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3243 {
3244   struct ElementInfo *ei;
3245   int chunk_size_expected;
3246   int element;
3247   int i, j, x, y;
3248
3249   // ---------- custom element base property values (96 bytes) ----------------
3250
3251   element = getMappedElement(getFile16BitBE(file));
3252
3253   if (!IS_CUSTOM_ELEMENT(element))
3254   {
3255     Warn("invalid custom element number %d", element);
3256
3257     ReadUnusedBytesFromFile(file, chunk_size - 2);
3258
3259     return chunk_size;
3260   }
3261
3262   ei = &element_info[element];
3263
3264   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3265     ei->description[i] = getFile8Bit(file);
3266   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3267
3268   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3269
3270   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3271
3272   ei->num_change_pages = getFile8Bit(file);
3273
3274   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3275   if (chunk_size_expected != chunk_size)
3276   {
3277     ReadUnusedBytesFromFile(file, chunk_size - 43);
3278     return chunk_size_expected;
3279   }
3280
3281   ei->ce_value_fixed_initial = getFile16BitBE(file);
3282   ei->ce_value_random_initial = getFile16BitBE(file);
3283   ei->use_last_ce_value = getFile8Bit(file);
3284
3285   ei->use_gfx_element = getFile8Bit(file);
3286   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3287
3288   ei->collect_score_initial = getFile8Bit(file);
3289   ei->collect_count_initial = getFile8Bit(file);
3290
3291   ei->drop_delay_fixed = getFile8Bit(file);
3292   ei->push_delay_fixed = getFile8Bit(file);
3293   ei->drop_delay_random = getFile8Bit(file);
3294   ei->push_delay_random = getFile8Bit(file);
3295   ei->move_delay_fixed = getFile16BitBE(file);
3296   ei->move_delay_random = getFile16BitBE(file);
3297
3298   // bits 0 - 15 of "move_pattern" ...
3299   ei->move_pattern = getFile16BitBE(file);
3300   ei->move_direction_initial = getFile8Bit(file);
3301   ei->move_stepsize = getFile8Bit(file);
3302
3303   ei->slippery_type = getFile8Bit(file);
3304
3305   for (y = 0; y < 3; y++)
3306     for (x = 0; x < 3; x++)
3307       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3308
3309   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3310   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3311   ei->move_leave_type = getFile8Bit(file);
3312
3313   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3314   ei->move_pattern |= (getFile16BitBE(file) << 16);
3315
3316   ei->access_direction = getFile8Bit(file);
3317
3318   ei->explosion_delay = getFile8Bit(file);
3319   ei->ignition_delay = getFile8Bit(file);
3320   ei->explosion_type = getFile8Bit(file);
3321
3322   // some free bytes for future custom property values and padding
3323   ReadUnusedBytesFromFile(file, 1);
3324
3325   // ---------- change page property values (48 bytes) ------------------------
3326
3327   setElementChangePages(ei, ei->num_change_pages);
3328
3329   for (i = 0; i < ei->num_change_pages; i++)
3330   {
3331     struct ElementChangeInfo *change = &ei->change_page[i];
3332     unsigned int event_bits;
3333
3334     // always start with reliable default values
3335     setElementChangeInfoToDefaults(change);
3336
3337     // bits 0 - 31 of "has_event[]" ...
3338     event_bits = getFile32BitBE(file);
3339     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3340       if (event_bits & (1u << j))
3341         change->has_event[j] = TRUE;
3342
3343     change->target_element = getMappedElement(getFile16BitBE(file));
3344
3345     change->delay_fixed = getFile16BitBE(file);
3346     change->delay_random = getFile16BitBE(file);
3347     change->delay_frames = getFile16BitBE(file);
3348
3349     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3350
3351     change->explode = getFile8Bit(file);
3352     change->use_target_content = getFile8Bit(file);
3353     change->only_if_complete = getFile8Bit(file);
3354     change->use_random_replace = getFile8Bit(file);
3355
3356     change->random_percentage = getFile8Bit(file);
3357     change->replace_when = getFile8Bit(file);
3358
3359     for (y = 0; y < 3; y++)
3360       for (x = 0; x < 3; x++)
3361         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3362
3363     change->can_change = getFile8Bit(file);
3364
3365     change->trigger_side = getFile8Bit(file);
3366
3367     change->trigger_player = getFile8Bit(file);
3368     change->trigger_page = getFile8Bit(file);
3369
3370     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3371                             CH_PAGE_ANY : (1 << change->trigger_page));
3372
3373     change->has_action = getFile8Bit(file);
3374     change->action_type = getFile8Bit(file);
3375     change->action_mode = getFile8Bit(file);
3376     change->action_arg = getFile16BitBE(file);
3377
3378     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3379     event_bits = getFile8Bit(file);
3380     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3381       if (event_bits & (1u << (j - 32)))
3382         change->has_event[j] = TRUE;
3383   }
3384
3385   // mark this custom element as modified
3386   ei->modified_settings = TRUE;
3387
3388   level->file_has_custom_elements = TRUE;
3389
3390   return chunk_size;
3391 }
3392
3393 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3394 {
3395   struct ElementInfo *ei;
3396   struct ElementGroupInfo *group;
3397   int element;
3398   int i;
3399
3400   element = getMappedElement(getFile16BitBE(file));
3401
3402   if (!IS_GROUP_ELEMENT(element))
3403   {
3404     Warn("invalid group element number %d", element);
3405
3406     ReadUnusedBytesFromFile(file, chunk_size - 2);
3407
3408     return chunk_size;
3409   }
3410
3411   ei = &element_info[element];
3412
3413   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3414     ei->description[i] = getFile8Bit(file);
3415   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3416
3417   group = element_info[element].group;
3418
3419   group->num_elements = getFile8Bit(file);
3420
3421   ei->use_gfx_element = getFile8Bit(file);
3422   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3423
3424   group->choice_mode = getFile8Bit(file);
3425
3426   // some free bytes for future values and padding
3427   ReadUnusedBytesFromFile(file, 3);
3428
3429   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3430     group->element[i] = getMappedElement(getFile16BitBE(file));
3431
3432   // mark this group element as modified
3433   element_info[element].modified_settings = TRUE;
3434
3435   level->file_has_custom_elements = TRUE;
3436
3437   return chunk_size;
3438 }
3439
3440 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3441                                 int element, int real_element)
3442 {
3443   int micro_chunk_size = 0;
3444   int conf_type = getFile8Bit(file);
3445   int byte_mask = conf_type & CONF_MASK_BYTES;
3446   boolean element_found = FALSE;
3447   int i;
3448
3449   micro_chunk_size += 1;
3450
3451   if (byte_mask == CONF_MASK_MULTI_BYTES)
3452   {
3453     int num_bytes = getFile16BitBE(file);
3454     byte *buffer = checked_malloc(num_bytes);
3455
3456     ReadBytesFromFile(file, buffer, num_bytes);
3457
3458     for (i = 0; conf[i].data_type != -1; i++)
3459     {
3460       if (conf[i].element == element &&
3461           conf[i].conf_type == conf_type)
3462       {
3463         int data_type = conf[i].data_type;
3464         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3465         int max_num_entities = conf[i].max_num_entities;
3466
3467         if (num_entities > max_num_entities)
3468         {
3469           Warn("truncating number of entities for element %d from %d to %d",
3470                element, num_entities, max_num_entities);
3471
3472           num_entities = max_num_entities;
3473         }
3474
3475         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3476                                   data_type == TYPE_CONTENT_LIST))
3477         {
3478           // for element and content lists, zero entities are not allowed
3479           Warn("found empty list of entities for element %d", element);
3480
3481           // do not set "num_entities" here to prevent reading behind buffer
3482
3483           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3484         }
3485         else
3486         {
3487           *(int *)(conf[i].num_entities) = num_entities;
3488         }
3489
3490         element_found = TRUE;
3491
3492         if (data_type == TYPE_STRING)
3493         {
3494           char *string = (char *)(conf[i].value);
3495           int j;
3496
3497           for (j = 0; j < max_num_entities; j++)
3498             string[j] = (j < num_entities ? buffer[j] : '\0');
3499         }
3500         else if (data_type == TYPE_ELEMENT_LIST)
3501         {
3502           int *element_array = (int *)(conf[i].value);
3503           int j;
3504
3505           for (j = 0; j < num_entities; j++)
3506             element_array[j] =
3507               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3508         }
3509         else if (data_type == TYPE_CONTENT_LIST)
3510         {
3511           struct Content *content= (struct Content *)(conf[i].value);
3512           int c, x, y;
3513
3514           for (c = 0; c < num_entities; c++)
3515             for (y = 0; y < 3; y++)
3516               for (x = 0; x < 3; x++)
3517                 content[c].e[x][y] =
3518                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3519         }
3520         else
3521           element_found = FALSE;
3522
3523         break;
3524       }
3525     }
3526
3527     checked_free(buffer);
3528
3529     micro_chunk_size += 2 + num_bytes;
3530   }
3531   else          // constant size configuration data (1, 2 or 4 bytes)
3532   {
3533     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3534                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3535                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3536
3537     for (i = 0; conf[i].data_type != -1; i++)
3538     {
3539       if (conf[i].element == element &&
3540           conf[i].conf_type == conf_type)
3541       {
3542         int data_type = conf[i].data_type;
3543
3544         if (data_type == TYPE_ELEMENT)
3545           value = getMappedElement(value);
3546
3547         if (data_type == TYPE_BOOLEAN)
3548           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3549         else
3550           *(int *)    (conf[i].value) = value;
3551
3552         element_found = TRUE;
3553
3554         break;
3555       }
3556     }
3557
3558     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3559   }
3560
3561   if (!element_found)
3562   {
3563     char *error_conf_chunk_bytes =
3564       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3565        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3566        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3567     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3568     int error_element = real_element;
3569
3570     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3571          error_conf_chunk_bytes, error_conf_chunk_token,
3572          error_element, EL_NAME(error_element));
3573   }
3574
3575   return micro_chunk_size;
3576 }
3577
3578 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3579 {
3580   int real_chunk_size = 0;
3581
3582   li = *level;          // copy level data into temporary buffer
3583
3584   while (!checkEndOfFile(file))
3585   {
3586     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3587
3588     if (real_chunk_size >= chunk_size)
3589       break;
3590   }
3591
3592   *level = li;          // copy temporary buffer back to level data
3593
3594   return real_chunk_size;
3595 }
3596
3597 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3598 {
3599   int real_chunk_size = 0;
3600
3601   li = *level;          // copy level data into temporary buffer
3602
3603   while (!checkEndOfFile(file))
3604   {
3605     int element = getMappedElement(getFile16BitBE(file));
3606
3607     real_chunk_size += 2;
3608     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3609                                             element, element);
3610     if (real_chunk_size >= chunk_size)
3611       break;
3612   }
3613
3614   *level = li;          // copy temporary buffer back to level data
3615
3616   return real_chunk_size;
3617 }
3618
3619 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3620 {
3621   int real_chunk_size = 0;
3622
3623   li = *level;          // copy level data into temporary buffer
3624
3625   while (!checkEndOfFile(file))
3626   {
3627     int element = getMappedElement(getFile16BitBE(file));
3628
3629     real_chunk_size += 2;
3630     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3631                                             element, element);
3632     if (real_chunk_size >= chunk_size)
3633       break;
3634   }
3635
3636   *level = li;          // copy temporary buffer back to level data
3637
3638   return real_chunk_size;
3639 }
3640
3641 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3642 {
3643   int element = getMappedElement(getFile16BitBE(file));
3644   int envelope_nr = element - EL_ENVELOPE_1;
3645   int real_chunk_size = 2;
3646
3647   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3648
3649   while (!checkEndOfFile(file))
3650   {
3651     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3652                                             -1, element);
3653
3654     if (real_chunk_size >= chunk_size)
3655       break;
3656   }
3657
3658   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3659
3660   return real_chunk_size;
3661 }
3662
3663 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3664 {
3665   int element = getMappedElement(getFile16BitBE(file));
3666   int real_chunk_size = 2;
3667   struct ElementInfo *ei = &element_info[element];
3668   int i;
3669
3670   xx_ei = *ei;          // copy element data into temporary buffer
3671
3672   xx_ei.num_change_pages = -1;
3673
3674   while (!checkEndOfFile(file))
3675   {
3676     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3677                                             -1, element);
3678     if (xx_ei.num_change_pages != -1)
3679       break;
3680
3681     if (real_chunk_size >= chunk_size)
3682       break;
3683   }
3684
3685   *ei = xx_ei;
3686
3687   if (ei->num_change_pages == -1)
3688   {
3689     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3690          EL_NAME(element));
3691
3692     ei->num_change_pages = 1;
3693
3694     setElementChangePages(ei, 1);
3695     setElementChangeInfoToDefaults(ei->change);
3696
3697     return real_chunk_size;
3698   }
3699
3700   // initialize number of change pages stored for this custom element
3701   setElementChangePages(ei, ei->num_change_pages);
3702   for (i = 0; i < ei->num_change_pages; i++)
3703     setElementChangeInfoToDefaults(&ei->change_page[i]);
3704
3705   // start with reading properties for the first change page
3706   xx_current_change_page = 0;
3707
3708   while (!checkEndOfFile(file))
3709   {
3710     // level file might contain invalid change page number
3711     if (xx_current_change_page >= ei->num_change_pages)
3712       break;
3713
3714     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3715
3716     xx_change = *change;        // copy change data into temporary buffer
3717
3718     resetEventBits();           // reset bits; change page might have changed
3719
3720     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3721                                             -1, element);
3722
3723     *change = xx_change;
3724
3725     setEventFlagsFromEventBits(change);
3726
3727     if (real_chunk_size >= chunk_size)
3728       break;
3729   }
3730
3731   level->file_has_custom_elements = TRUE;
3732
3733   return real_chunk_size;
3734 }
3735
3736 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3737 {
3738   int element = getMappedElement(getFile16BitBE(file));
3739   int real_chunk_size = 2;
3740   struct ElementInfo *ei = &element_info[element];
3741   struct ElementGroupInfo *group = ei->group;
3742
3743   if (group == NULL)
3744     return -1;
3745
3746   xx_ei = *ei;          // copy element data into temporary buffer
3747   xx_group = *group;    // copy group data into temporary buffer
3748
3749   while (!checkEndOfFile(file))
3750   {
3751     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3752                                             -1, element);
3753
3754     if (real_chunk_size >= chunk_size)
3755       break;
3756   }
3757
3758   *ei = xx_ei;
3759   *group = xx_group;
3760
3761   level->file_has_custom_elements = TRUE;
3762
3763   return real_chunk_size;
3764 }
3765
3766 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3767 {
3768   int element = getMappedElement(getFile16BitBE(file));
3769   int real_chunk_size = 2;
3770   struct ElementInfo *ei = &element_info[element];
3771
3772   xx_ei = *ei;          // copy element data into temporary buffer
3773
3774   while (!checkEndOfFile(file))
3775   {
3776     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3777                                             -1, element);
3778
3779     if (real_chunk_size >= chunk_size)
3780       break;
3781   }
3782
3783   *ei = xx_ei;
3784
3785   level->file_has_custom_elements = TRUE;
3786
3787   return real_chunk_size;
3788 }
3789
3790 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3791                                       struct LevelFileInfo *level_file_info,
3792                                       boolean level_info_only)
3793 {
3794   char *filename = level_file_info->filename;
3795   char cookie[MAX_LINE_LEN];
3796   char chunk_name[CHUNK_ID_LEN + 1];
3797   int chunk_size;
3798   File *file;
3799
3800   if (!(file = openFile(filename, MODE_READ)))
3801   {
3802     level->no_valid_file = TRUE;
3803     level->no_level_file = TRUE;
3804
3805     if (level_info_only)
3806       return;
3807
3808     Warn("cannot read level '%s' -- using empty level", filename);
3809
3810     if (!setup.editor.use_template_for_new_levels)
3811       return;
3812
3813     // if level file not found, try to initialize level data from template
3814     filename = getGlobalLevelTemplateFilename();
3815
3816     if (!(file = openFile(filename, MODE_READ)))
3817       return;
3818
3819     // default: for empty levels, use level template for custom elements
3820     level->use_custom_template = TRUE;
3821
3822     level->no_valid_file = FALSE;
3823   }
3824
3825   getFileChunkBE(file, chunk_name, NULL);
3826   if (strEqual(chunk_name, "RND1"))
3827   {
3828     getFile32BitBE(file);               // not used
3829
3830     getFileChunkBE(file, chunk_name, NULL);
3831     if (!strEqual(chunk_name, "CAVE"))
3832     {
3833       level->no_valid_file = TRUE;
3834
3835       Warn("unknown format of level file '%s'", filename);
3836
3837       closeFile(file);
3838
3839       return;
3840     }
3841   }
3842   else  // check for pre-2.0 file format with cookie string
3843   {
3844     strcpy(cookie, chunk_name);
3845     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3846       cookie[4] = '\0';
3847     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3848       cookie[strlen(cookie) - 1] = '\0';
3849
3850     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3851     {
3852       level->no_valid_file = TRUE;
3853
3854       Warn("unknown format of level file '%s'", filename);
3855
3856       closeFile(file);
3857
3858       return;
3859     }
3860
3861     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3862     {
3863       level->no_valid_file = TRUE;
3864
3865       Warn("unsupported version of level file '%s'", filename);
3866
3867       closeFile(file);
3868
3869       return;
3870     }
3871
3872     // pre-2.0 level files have no game version, so use file version here
3873     level->game_version = level->file_version;
3874   }
3875
3876   if (level->file_version < FILE_VERSION_1_2)
3877   {
3878     // level files from versions before 1.2.0 without chunk structure
3879     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3880     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3881   }
3882   else
3883   {
3884     static struct
3885     {
3886       char *name;
3887       int size;
3888       int (*loader)(File *, int, struct LevelInfo *);
3889     }
3890     chunk_info[] =
3891     {
3892       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3893       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3894       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3895       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3896       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3897       { "INFO", -1,                     LoadLevel_INFO },
3898       { "BODY", -1,                     LoadLevel_BODY },
3899       { "CONT", -1,                     LoadLevel_CONT },
3900       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3901       { "CNT3", -1,                     LoadLevel_CNT3 },
3902       { "CUS1", -1,                     LoadLevel_CUS1 },
3903       { "CUS2", -1,                     LoadLevel_CUS2 },
3904       { "CUS3", -1,                     LoadLevel_CUS3 },
3905       { "CUS4", -1,                     LoadLevel_CUS4 },
3906       { "GRP1", -1,                     LoadLevel_GRP1 },
3907       { "CONF", -1,                     LoadLevel_CONF },
3908       { "ELEM", -1,                     LoadLevel_ELEM },
3909       { "NOTE", -1,                     LoadLevel_NOTE },
3910       { "CUSX", -1,                     LoadLevel_CUSX },
3911       { "GRPX", -1,                     LoadLevel_GRPX },
3912       { "EMPX", -1,                     LoadLevel_EMPX },
3913
3914       {  NULL,  0,                      NULL }
3915     };
3916
3917     while (getFileChunkBE(file, chunk_name, &chunk_size))
3918     {
3919       int i = 0;
3920
3921       while (chunk_info[i].name != NULL &&
3922              !strEqual(chunk_name, chunk_info[i].name))
3923         i++;
3924
3925       if (chunk_info[i].name == NULL)
3926       {
3927         Warn("unknown chunk '%s' in level file '%s'",
3928              chunk_name, filename);
3929
3930         ReadUnusedBytesFromFile(file, chunk_size);
3931       }
3932       else if (chunk_info[i].size != -1 &&
3933                chunk_info[i].size != chunk_size)
3934       {
3935         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3936              chunk_size, chunk_name, filename);
3937
3938         ReadUnusedBytesFromFile(file, chunk_size);
3939       }
3940       else
3941       {
3942         // call function to load this level chunk
3943         int chunk_size_expected =
3944           (chunk_info[i].loader)(file, chunk_size, level);
3945
3946         if (chunk_size_expected < 0)
3947         {
3948           Warn("error reading chunk '%s' in level file '%s'",
3949                chunk_name, filename);
3950
3951           break;
3952         }
3953
3954         // the size of some chunks cannot be checked before reading other
3955         // chunks first (like "HEAD" and "BODY") that contain some header
3956         // information, so check them here
3957         if (chunk_size_expected != chunk_size)
3958         {
3959           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3960                chunk_size, chunk_name, filename);
3961
3962           break;
3963         }
3964       }
3965     }
3966   }
3967
3968   closeFile(file);
3969 }
3970
3971
3972 // ----------------------------------------------------------------------------
3973 // functions for loading BD level
3974 // ----------------------------------------------------------------------------
3975
3976 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3977 {
3978   struct LevelInfo_BD *level_bd = level->native_bd_level;
3979   GdCave *cave = NULL;  // will be changed below
3980   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3981   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3982   int x, y;
3983
3984   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3985
3986   // cave and map newly allocated when set to defaults above
3987   cave = level_bd->cave;
3988
3989   // level type
3990   cave->intermission                    = level->bd_intermission;
3991
3992   // level settings
3993   cave->level_time[0]                   = level->time;
3994   cave->level_diamonds[0]               = level->gems_needed;
3995
3996   // game timing
3997   cave->scheduling                      = level->bd_scheduling_type;
3998   cave->pal_timing                      = level->bd_pal_timing;
3999   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4000   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4001   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4002   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4003
4004   // scores
4005   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4006   cave->diamond_value                   = level->score[SC_EMERALD];
4007   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4008
4009   // compatibility settings
4010   cave->lineshift                       = level->bd_line_shifting_borders;
4011   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4012   cave->short_explosions                = level->bd_short_explosions;
4013   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4014
4015   // player properties
4016   cave->diagonal_movements              = level->bd_diagonal_movements;
4017   cave->active_is_first_found           = level->bd_topmost_player_active;
4018   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4019   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4020   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4021   cave->snap_element                    = map_element_RND_to_BD(level->bd_snap_element);
4022
4023   // element properties
4024   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4025   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4026   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4027   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4028   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4029   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4030   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4031   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4032   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4033   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4034   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4035   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4036   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4037   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4038   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4039   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4040   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4041   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4042   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4043   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4044
4045   cave->amoeba_too_big_effect       = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
4046   cave->amoeba_enclosed_effect      = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
4047   cave->amoeba_2_too_big_effect     = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
4048   cave->amoeba_2_enclosed_effect    = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
4049   cave->amoeba_2_explosion_effect   = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
4050   cave->amoeba_2_looks_like         = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
4051
4052   cave->slime_predictable               = level->bd_slime_is_predictable;
4053   cave->slime_correct_random            = level->bd_slime_correct_random;
4054   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4055   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4056   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4057   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4058
4059   cave->acid_eats_this                  = map_element_RND_to_BD(level->bd_acid_eats_element);
4060   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4061   cave->acid_turns_to                   = map_element_RND_to_BD(level->bd_acid_turns_to_element);
4062
4063   cave->biter_delay_frame               = level->bd_biter_move_delay;
4064   cave->biter_eat                       = map_element_RND_to_BD(level->bd_biter_eats_element);
4065
4066   cave->bladder_converts_by         = map_element_RND_to_BD(level->bd_bladder_converts_by_element);
4067
4068   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4069
4070   cave->replicators_active              = level->bd_replicators_active;
4071   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4072
4073   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4074   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4075
4076   // level name
4077   strncpy(cave->name, level->name, sizeof(GdString));
4078   cave->name[sizeof(GdString) - 1] = '\0';
4079
4080   // playfield elements
4081   for (x = 0; x < cave->w; x++)
4082     for (y = 0; y < cave->h; y++)
4083       cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
4084 }
4085
4086 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4087 {
4088   struct LevelInfo_BD *level_bd = level->native_bd_level;
4089   GdCave *cave = level_bd->cave;
4090   int bd_level_nr = level_bd->level_nr;
4091   int x, y;
4092
4093   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4094   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4095
4096   // level type
4097   level->bd_intermission                = cave->intermission;
4098
4099   // level settings
4100   level->time                           = cave->level_time[bd_level_nr];
4101   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4102
4103   // game timing
4104   level->bd_scheduling_type             = cave->scheduling;
4105   level->bd_pal_timing                  = cave->pal_timing;
4106   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4107   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4108   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4109   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4110
4111   // scores
4112   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4113   level->score[SC_EMERALD]              = cave->diamond_value;
4114   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4115
4116   // compatibility settings
4117   level->bd_line_shifting_borders       = cave->lineshift;
4118   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4119   level->bd_short_explosions            = cave->short_explosions;
4120   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4121
4122   // player properties
4123   level->bd_diagonal_movements          = cave->diagonal_movements;
4124   level->bd_topmost_player_active       = cave->active_is_first_found;
4125   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4126   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4127   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4128   level->bd_snap_element                = map_element_BD_to_RND(cave->snap_element);
4129
4130   // element properties
4131   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4132   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4133   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4134   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4135   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4136   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4137   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4138   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4139   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4140   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4141   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4142   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4143   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4144   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4145   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4146   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4147   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4148   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4149   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4150   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4151
4152   level->bd_amoeba_content_too_big      = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4153   level->bd_amoeba_content_enclosed     = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4154   level->bd_amoeba_2_content_too_big    = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4155   level->bd_amoeba_2_content_enclosed   = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4156   level->bd_amoeba_2_content_exploding  = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4157   level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4158
4159   level->bd_slime_is_predictable        = cave->slime_predictable;
4160   level->bd_slime_correct_random        = cave->slime_correct_random;
4161   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4162   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4163   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4164   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4165
4166   level->bd_acid_eats_element           = map_element_BD_to_RND(cave->acid_eats_this);
4167   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4168   level->bd_acid_turns_to_element       = map_element_BD_to_RND(cave->acid_turns_to);
4169
4170   level->bd_biter_move_delay            = cave->biter_delay_frame;
4171   level->bd_biter_eats_element          = map_element_BD_to_RND(cave->biter_eat);
4172
4173   level->bd_bladder_converts_by_element = map_element_BD_to_RND(cave->bladder_converts_by);
4174
4175   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4176
4177   level->bd_replicators_active          = cave->replicators_active;
4178   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4179
4180   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4181   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4182
4183   // level name
4184   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4185
4186   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4187   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4188
4189   // playfield elements
4190   for (x = 0; x < level->fieldx; x++)
4191     for (y = 0; y < level->fieldy; y++)
4192       level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4193
4194   checked_free(cave_name);
4195 }
4196
4197 static void setTapeInfoToDefaults(void);
4198
4199 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4200 {
4201   struct LevelInfo_BD *level_bd = level->native_bd_level;
4202   GdCave *cave = level_bd->cave;
4203   GdReplay *replay = level_bd->replay;
4204   int i;
4205
4206   if (replay == NULL)
4207     return;
4208
4209   // always start with reliable default values
4210   setTapeInfoToDefaults();
4211
4212   tape.level_nr = level_nr;             // (currently not used)
4213   tape.random_seed = replay->seed;
4214
4215   TapeSetDateFromIsoDateString(replay->date);
4216
4217   tape.counter = 0;
4218   tape.pos[tape.counter].delay = 0;
4219
4220   tape.bd_replay = TRUE;
4221
4222   // all time calculations only used to display approximate tape time
4223   int cave_speed = cave->speed;
4224   int milliseconds_game = 0;
4225   int milliseconds_elapsed = 20;
4226
4227   for (i = 0; i < replay->movements->len; i++)
4228   {
4229     int replay_action = replay->movements->data[i];
4230     int tape_action = map_action_BD_to_RND(replay_action);
4231     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4232     boolean success = 0;
4233
4234     while (1)
4235     {
4236       success = TapeAddAction(action);
4237
4238       milliseconds_game += milliseconds_elapsed;
4239
4240       if (milliseconds_game >= cave_speed)
4241       {
4242         milliseconds_game -= cave_speed;
4243
4244         break;
4245       }
4246     }
4247
4248     tape.counter++;
4249     tape.pos[tape.counter].delay = 0;
4250     tape.pos[tape.counter].action[0] = 0;
4251
4252     if (!success)
4253     {
4254       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4255
4256       break;
4257     }
4258   }
4259
4260   TapeHaltRecording();
4261 }
4262
4263
4264 // ----------------------------------------------------------------------------
4265 // functions for loading EM level
4266 // ----------------------------------------------------------------------------
4267
4268 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4269 {
4270   static int ball_xy[8][2] =
4271   {
4272     { 0, 0 },
4273     { 1, 0 },
4274     { 2, 0 },
4275     { 0, 1 },
4276     { 2, 1 },
4277     { 0, 2 },
4278     { 1, 2 },
4279     { 2, 2 },
4280   };
4281   struct LevelInfo_EM *level_em = level->native_em_level;
4282   struct CAVE *cav = level_em->cav;
4283   int i, j, x, y;
4284
4285   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4286   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4287
4288   cav->time_seconds     = level->time;
4289   cav->gems_needed      = level->gems_needed;
4290
4291   cav->emerald_score    = level->score[SC_EMERALD];
4292   cav->diamond_score    = level->score[SC_DIAMOND];
4293   cav->alien_score      = level->score[SC_ROBOT];
4294   cav->tank_score       = level->score[SC_SPACESHIP];
4295   cav->bug_score        = level->score[SC_BUG];
4296   cav->eater_score      = level->score[SC_YAMYAM];
4297   cav->nut_score        = level->score[SC_NUT];
4298   cav->dynamite_score   = level->score[SC_DYNAMITE];
4299   cav->key_score        = level->score[SC_KEY];
4300   cav->exit_score       = level->score[SC_TIME_BONUS];
4301
4302   cav->num_eater_arrays = level->num_yamyam_contents;
4303
4304   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4305     for (y = 0; y < 3; y++)
4306       for (x = 0; x < 3; x++)
4307         cav->eater_array[i][y * 3 + x] =
4308           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4309
4310   cav->amoeba_time              = level->amoeba_speed;
4311   cav->wonderwall_time          = level->time_magic_wall;
4312   cav->wheel_time               = level->time_wheel;
4313
4314   cav->android_move_time        = level->android_move_time;
4315   cav->android_clone_time       = level->android_clone_time;
4316   cav->ball_random              = level->ball_random;
4317   cav->ball_active              = level->ball_active_initial;
4318   cav->ball_time                = level->ball_time;
4319   cav->num_ball_arrays          = level->num_ball_contents;
4320
4321   cav->lenses_score             = level->lenses_score;
4322   cav->magnify_score            = level->magnify_score;
4323   cav->slurp_score              = level->slurp_score;
4324
4325   cav->lenses_time              = level->lenses_time;
4326   cav->magnify_time             = level->magnify_time;
4327
4328   cav->wind_time = 9999;
4329   cav->wind_direction =
4330     map_direction_RND_to_EM(level->wind_direction_initial);
4331
4332   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4333     for (j = 0; j < 8; j++)
4334       cav->ball_array[i][j] =
4335         map_element_RND_to_EM_cave(level->ball_content[i].
4336                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4337
4338   map_android_clone_elements_RND_to_EM(level);
4339
4340   // first fill the complete playfield with the empty space element
4341   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4342     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4343       cav->cave[x][y] = Cblank;
4344
4345   // then copy the real level contents from level file into the playfield
4346   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4347   {
4348     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4349
4350     if (level->field[x][y] == EL_AMOEBA_DEAD)
4351       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4352
4353     cav->cave[x][y] = new_element;
4354   }
4355
4356   for (i = 0; i < MAX_PLAYERS; i++)
4357   {
4358     cav->player_x[i] = -1;
4359     cav->player_y[i] = -1;
4360   }
4361
4362   // initialize player positions and delete players from the playfield
4363   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4364   {
4365     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4366     {
4367       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4368
4369       cav->player_x[player_nr] = x;
4370       cav->player_y[player_nr] = y;
4371
4372       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4373     }
4374   }
4375 }
4376
4377 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4378 {
4379   static int ball_xy[8][2] =
4380   {
4381     { 0, 0 },
4382     { 1, 0 },
4383     { 2, 0 },
4384     { 0, 1 },
4385     { 2, 1 },
4386     { 0, 2 },
4387     { 1, 2 },
4388     { 2, 2 },
4389   };
4390   struct LevelInfo_EM *level_em = level->native_em_level;
4391   struct CAVE *cav = level_em->cav;
4392   int i, j, x, y;
4393
4394   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4395   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4396
4397   level->time        = cav->time_seconds;
4398   level->gems_needed = cav->gems_needed;
4399
4400   sprintf(level->name, "Level %d", level->file_info.nr);
4401
4402   level->score[SC_EMERALD]      = cav->emerald_score;
4403   level->score[SC_DIAMOND]      = cav->diamond_score;
4404   level->score[SC_ROBOT]        = cav->alien_score;
4405   level->score[SC_SPACESHIP]    = cav->tank_score;
4406   level->score[SC_BUG]          = cav->bug_score;
4407   level->score[SC_YAMYAM]       = cav->eater_score;
4408   level->score[SC_NUT]          = cav->nut_score;
4409   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4410   level->score[SC_KEY]          = cav->key_score;
4411   level->score[SC_TIME_BONUS]   = cav->exit_score;
4412
4413   level->num_yamyam_contents    = cav->num_eater_arrays;
4414
4415   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4416     for (y = 0; y < 3; y++)
4417       for (x = 0; x < 3; x++)
4418         level->yamyam_content[i].e[x][y] =
4419           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4420
4421   level->amoeba_speed           = cav->amoeba_time;
4422   level->time_magic_wall        = cav->wonderwall_time;
4423   level->time_wheel             = cav->wheel_time;
4424
4425   level->android_move_time      = cav->android_move_time;
4426   level->android_clone_time     = cav->android_clone_time;
4427   level->ball_random            = cav->ball_random;
4428   level->ball_active_initial    = cav->ball_active;
4429   level->ball_time              = cav->ball_time;
4430   level->num_ball_contents      = cav->num_ball_arrays;
4431
4432   level->lenses_score           = cav->lenses_score;
4433   level->magnify_score          = cav->magnify_score;
4434   level->slurp_score            = cav->slurp_score;
4435
4436   level->lenses_time            = cav->lenses_time;
4437   level->magnify_time           = cav->magnify_time;
4438
4439   level->wind_direction_initial =
4440     map_direction_EM_to_RND(cav->wind_direction);
4441
4442   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4443     for (j = 0; j < 8; j++)
4444       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4445         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4446
4447   map_android_clone_elements_EM_to_RND(level);
4448
4449   // convert the playfield (some elements need special treatment)
4450   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4451   {
4452     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4453
4454     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4455       new_element = EL_AMOEBA_DEAD;
4456
4457     level->field[x][y] = new_element;
4458   }
4459
4460   for (i = 0; i < MAX_PLAYERS; i++)
4461   {
4462     // in case of all players set to the same field, use the first player
4463     int nr = MAX_PLAYERS - i - 1;
4464     int jx = cav->player_x[nr];
4465     int jy = cav->player_y[nr];
4466
4467     if (jx != -1 && jy != -1)
4468       level->field[jx][jy] = EL_PLAYER_1 + nr;
4469   }
4470
4471   // time score is counted for each 10 seconds left in Emerald Mine levels
4472   level->time_score_base = 10;
4473 }
4474
4475
4476 // ----------------------------------------------------------------------------
4477 // functions for loading SP level
4478 // ----------------------------------------------------------------------------
4479
4480 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4481 {
4482   struct LevelInfo_SP *level_sp = level->native_sp_level;
4483   LevelInfoType *header = &level_sp->header;
4484   int i, x, y;
4485
4486   level_sp->width  = level->fieldx;
4487   level_sp->height = level->fieldy;
4488
4489   for (x = 0; x < level->fieldx; x++)
4490     for (y = 0; y < level->fieldy; y++)
4491       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4492
4493   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4494
4495   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4496     header->LevelTitle[i] = level->name[i];
4497   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4498
4499   header->InfotronsNeeded = level->gems_needed;
4500
4501   header->SpecialPortCount = 0;
4502
4503   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4504   {
4505     boolean gravity_port_found = FALSE;
4506     boolean gravity_port_valid = FALSE;
4507     int gravity_port_flag;
4508     int gravity_port_base_element;
4509     int element = level->field[x][y];
4510
4511     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4512         element <= EL_SP_GRAVITY_ON_PORT_UP)
4513     {
4514       gravity_port_found = TRUE;
4515       gravity_port_valid = TRUE;
4516       gravity_port_flag = 1;
4517       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4518     }
4519     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4520              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4521     {
4522       gravity_port_found = TRUE;
4523       gravity_port_valid = TRUE;
4524       gravity_port_flag = 0;
4525       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4526     }
4527     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4528              element <= EL_SP_GRAVITY_PORT_UP)
4529     {
4530       // change R'n'D style gravity inverting special port to normal port
4531       // (there are no gravity inverting ports in native Supaplex engine)
4532
4533       gravity_port_found = TRUE;
4534       gravity_port_valid = FALSE;
4535       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4536     }
4537
4538     if (gravity_port_found)
4539     {
4540       if (gravity_port_valid &&
4541           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4542       {
4543         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4544
4545         port->PortLocation = (y * level->fieldx + x) * 2;
4546         port->Gravity = gravity_port_flag;
4547
4548         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4549
4550         header->SpecialPortCount++;
4551       }
4552       else
4553       {
4554         // change special gravity port to normal port
4555
4556         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4557       }
4558
4559       level_sp->playfield[x][y] = element - EL_SP_START;
4560     }
4561   }
4562 }
4563
4564 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4565 {
4566   struct LevelInfo_SP *level_sp = level->native_sp_level;
4567   LevelInfoType *header = &level_sp->header;
4568   boolean num_invalid_elements = 0;
4569   int i, j, x, y;
4570
4571   level->fieldx = level_sp->width;
4572   level->fieldy = level_sp->height;
4573
4574   for (x = 0; x < level->fieldx; x++)
4575   {
4576     for (y = 0; y < level->fieldy; y++)
4577     {
4578       int element_old = level_sp->playfield[x][y];
4579       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4580
4581       if (element_new == EL_UNKNOWN)
4582       {
4583         num_invalid_elements++;
4584
4585         Debug("level:native:SP", "invalid element %d at position %d, %d",
4586               element_old, x, y);
4587       }
4588
4589       level->field[x][y] = element_new;
4590     }
4591   }
4592
4593   if (num_invalid_elements > 0)
4594     Warn("found %d invalid elements%s", num_invalid_elements,
4595          (!options.debug ? " (use '--debug' for more details)" : ""));
4596
4597   for (i = 0; i < MAX_PLAYERS; i++)
4598     level->initial_player_gravity[i] =
4599       (header->InitialGravity == 1 ? TRUE : FALSE);
4600
4601   // skip leading spaces
4602   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4603     if (header->LevelTitle[i] != ' ')
4604       break;
4605
4606   // copy level title
4607   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4608     level->name[j] = header->LevelTitle[i];
4609   level->name[j] = '\0';
4610
4611   // cut trailing spaces
4612   for (; j > 0; j--)
4613     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4614       level->name[j - 1] = '\0';
4615
4616   level->gems_needed = header->InfotronsNeeded;
4617
4618   for (i = 0; i < header->SpecialPortCount; i++)
4619   {
4620     SpecialPortType *port = &header->SpecialPort[i];
4621     int port_location = port->PortLocation;
4622     int gravity = port->Gravity;
4623     int port_x, port_y, port_element;
4624
4625     port_x = (port_location / 2) % level->fieldx;
4626     port_y = (port_location / 2) / level->fieldx;
4627
4628     if (port_x < 0 || port_x >= level->fieldx ||
4629         port_y < 0 || port_y >= level->fieldy)
4630     {
4631       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4632
4633       continue;
4634     }
4635
4636     port_element = level->field[port_x][port_y];
4637
4638     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4639         port_element > EL_SP_GRAVITY_PORT_UP)
4640     {
4641       Warn("no special port at position (%d, %d)", port_x, port_y);
4642
4643       continue;
4644     }
4645
4646     // change previous (wrong) gravity inverting special port to either
4647     // gravity enabling special port or gravity disabling special port
4648     level->field[port_x][port_y] +=
4649       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4650        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4651   }
4652
4653   // change special gravity ports without database entries to normal ports
4654   for (x = 0; x < level->fieldx; x++)
4655     for (y = 0; y < level->fieldy; y++)
4656       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4657           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4658         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4659
4660   level->time = 0;                      // no time limit
4661   level->amoeba_speed = 0;
4662   level->time_magic_wall = 0;
4663   level->time_wheel = 0;
4664   level->amoeba_content = EL_EMPTY;
4665
4666   // original Supaplex does not use score values -- rate by playing time
4667   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4668     level->score[i] = 0;
4669
4670   level->rate_time_over_score = TRUE;
4671
4672   // there are no yamyams in supaplex levels
4673   for (i = 0; i < level->num_yamyam_contents; i++)
4674     for (x = 0; x < 3; x++)
4675       for (y = 0; y < 3; y++)
4676         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4677 }
4678
4679 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4680 {
4681   struct LevelInfo_SP *level_sp = level->native_sp_level;
4682   struct DemoInfo_SP *demo = &level_sp->demo;
4683   int i, j;
4684
4685   // always start with reliable default values
4686   demo->is_available = FALSE;
4687   demo->length = 0;
4688
4689   if (TAPE_IS_EMPTY(tape))
4690     return;
4691
4692   demo->level_nr = tape.level_nr;       // (currently not used)
4693
4694   level_sp->header.DemoRandomSeed = tape.random_seed;
4695
4696   demo->length = 0;
4697
4698   for (i = 0; i < tape.length; i++)
4699   {
4700     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4701     int demo_repeat = tape.pos[i].delay;
4702     int demo_entries = (demo_repeat + 15) / 16;
4703
4704     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4705     {
4706       Warn("tape truncated: size exceeds maximum SP demo size %d",
4707            SP_MAX_TAPE_LEN);
4708
4709       break;
4710     }
4711
4712     for (j = 0; j < demo_repeat / 16; j++)
4713       demo->data[demo->length++] = 0xf0 | demo_action;
4714
4715     if (demo_repeat % 16)
4716       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4717   }
4718
4719   demo->is_available = TRUE;
4720 }
4721
4722 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4723 {
4724   struct LevelInfo_SP *level_sp = level->native_sp_level;
4725   struct DemoInfo_SP *demo = &level_sp->demo;
4726   char *filename = level->file_info.filename;
4727   int i;
4728
4729   // always start with reliable default values
4730   setTapeInfoToDefaults();
4731
4732   if (!demo->is_available)
4733     return;
4734
4735   tape.level_nr = demo->level_nr;       // (currently not used)
4736   tape.random_seed = level_sp->header.DemoRandomSeed;
4737
4738   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4739
4740   tape.counter = 0;
4741   tape.pos[tape.counter].delay = 0;
4742
4743   for (i = 0; i < demo->length; i++)
4744   {
4745     int demo_action = demo->data[i] & 0x0f;
4746     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4747     int tape_action = map_key_SP_to_RND(demo_action);
4748     int tape_repeat = demo_repeat + 1;
4749     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4750     boolean success = 0;
4751     int j;
4752
4753     for (j = 0; j < tape_repeat; j++)
4754       success = TapeAddAction(action);
4755
4756     if (!success)
4757     {
4758       Warn("SP demo truncated: size exceeds maximum tape size %d",
4759            MAX_TAPE_LEN);
4760
4761       break;
4762     }
4763   }
4764
4765   TapeHaltRecording();
4766 }
4767
4768
4769 // ----------------------------------------------------------------------------
4770 // functions for loading MM level
4771 // ----------------------------------------------------------------------------
4772
4773 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4774 {
4775   struct LevelInfo_MM *level_mm = level->native_mm_level;
4776   int i, x, y;
4777
4778   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4779   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4780
4781   level_mm->time = level->time;
4782   level_mm->kettles_needed = level->gems_needed;
4783   level_mm->auto_count_kettles = level->auto_count_gems;
4784
4785   level_mm->mm_laser_red   = level->mm_laser_red;
4786   level_mm->mm_laser_green = level->mm_laser_green;
4787   level_mm->mm_laser_blue  = level->mm_laser_blue;
4788
4789   level_mm->df_laser_red   = level->df_laser_red;
4790   level_mm->df_laser_green = level->df_laser_green;
4791   level_mm->df_laser_blue  = level->df_laser_blue;
4792
4793   strcpy(level_mm->name, level->name);
4794   strcpy(level_mm->author, level->author);
4795
4796   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4797   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4798   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4799   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4800   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4801
4802   level_mm->amoeba_speed = level->amoeba_speed;
4803   level_mm->time_fuse    = level->mm_time_fuse;
4804   level_mm->time_bomb    = level->mm_time_bomb;
4805   level_mm->time_ball    = level->mm_time_ball;
4806   level_mm->time_block   = level->mm_time_block;
4807
4808   level_mm->num_ball_contents = level->num_mm_ball_contents;
4809   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4810   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4811   level_mm->explode_ball = level->explode_mm_ball;
4812
4813   for (i = 0; i < level->num_mm_ball_contents; i++)
4814     level_mm->ball_content[i] =
4815       map_element_RND_to_MM(level->mm_ball_content[i]);
4816
4817   for (x = 0; x < level->fieldx; x++)
4818     for (y = 0; y < level->fieldy; y++)
4819       Ur[x][y] =
4820         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4821 }
4822
4823 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4824 {
4825   struct LevelInfo_MM *level_mm = level->native_mm_level;
4826   int i, x, y;
4827
4828   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4829   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4830
4831   level->time = level_mm->time;
4832   level->gems_needed = level_mm->kettles_needed;
4833   level->auto_count_gems = level_mm->auto_count_kettles;
4834
4835   level->mm_laser_red   = level_mm->mm_laser_red;
4836   level->mm_laser_green = level_mm->mm_laser_green;
4837   level->mm_laser_blue  = level_mm->mm_laser_blue;
4838
4839   level->df_laser_red   = level_mm->df_laser_red;
4840   level->df_laser_green = level_mm->df_laser_green;
4841   level->df_laser_blue  = level_mm->df_laser_blue;
4842
4843   strcpy(level->name, level_mm->name);
4844
4845   // only overwrite author from 'levelinfo.conf' if author defined in level
4846   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4847     strcpy(level->author, level_mm->author);
4848
4849   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4850   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4851   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4852   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4853   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4854
4855   level->amoeba_speed  = level_mm->amoeba_speed;
4856   level->mm_time_fuse  = level_mm->time_fuse;
4857   level->mm_time_bomb  = level_mm->time_bomb;
4858   level->mm_time_ball  = level_mm->time_ball;
4859   level->mm_time_block = level_mm->time_block;
4860
4861   level->num_mm_ball_contents = level_mm->num_ball_contents;
4862   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4863   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4864   level->explode_mm_ball = level_mm->explode_ball;
4865
4866   for (i = 0; i < level->num_mm_ball_contents; i++)
4867     level->mm_ball_content[i] =
4868       map_element_MM_to_RND(level_mm->ball_content[i]);
4869
4870   for (x = 0; x < level->fieldx; x++)
4871     for (y = 0; y < level->fieldy; y++)
4872       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4873 }
4874
4875
4876 // ----------------------------------------------------------------------------
4877 // functions for loading DC level
4878 // ----------------------------------------------------------------------------
4879
4880 #define DC_LEVEL_HEADER_SIZE            344
4881
4882 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4883                                         boolean init)
4884 {
4885   static int last_data_encoded;
4886   static int offset1;
4887   static int offset2;
4888   int diff;
4889   int diff_hi, diff_lo;
4890   int data_hi, data_lo;
4891   unsigned short data_decoded;
4892
4893   if (init)
4894   {
4895     last_data_encoded = 0;
4896     offset1 = -1;
4897     offset2 = 0;
4898
4899     return 0;
4900   }
4901
4902   diff = data_encoded - last_data_encoded;
4903   diff_hi = diff & ~0xff;
4904   diff_lo = diff &  0xff;
4905
4906   offset2 += diff_lo;
4907
4908   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4909   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4910   data_hi = data_hi & 0xff00;
4911
4912   data_decoded = data_hi | data_lo;
4913
4914   last_data_encoded = data_encoded;
4915
4916   offset1 = (offset1 + 1) % 31;
4917   offset2 = offset2 & 0xff;
4918
4919   return data_decoded;
4920 }
4921
4922 static int getMappedElement_DC(int element)
4923 {
4924   switch (element)
4925   {
4926     case 0x0000:
4927       element = EL_ROCK;
4928       break;
4929
4930       // 0x0117 - 0x036e: (?)
4931       // EL_DIAMOND
4932
4933       // 0x042d - 0x0684: (?)
4934       // EL_EMERALD
4935
4936     case 0x06f1:
4937       element = EL_NUT;
4938       break;
4939
4940     case 0x074c:
4941       element = EL_BOMB;
4942       break;
4943
4944     case 0x07a4:
4945       element = EL_PEARL;
4946       break;
4947
4948     case 0x0823:
4949       element = EL_CRYSTAL;
4950       break;
4951
4952     case 0x0e77:        // quicksand (boulder)
4953       element = EL_QUICKSAND_FAST_FULL;
4954       break;
4955
4956     case 0x0e99:        // slow quicksand (boulder)
4957       element = EL_QUICKSAND_FULL;
4958       break;
4959
4960     case 0x0ed2:
4961       element = EL_EM_EXIT_OPEN;
4962       break;
4963
4964     case 0x0ee3:
4965       element = EL_EM_EXIT_CLOSED;
4966       break;
4967
4968     case 0x0eeb:
4969       element = EL_EM_STEEL_EXIT_OPEN;
4970       break;
4971
4972     case 0x0efc:
4973       element = EL_EM_STEEL_EXIT_CLOSED;
4974       break;
4975
4976     case 0x0f4f:        // dynamite (lit 1)
4977       element = EL_EM_DYNAMITE_ACTIVE;
4978       break;
4979
4980     case 0x0f57:        // dynamite (lit 2)
4981       element = EL_EM_DYNAMITE_ACTIVE;
4982       break;
4983
4984     case 0x0f5f:        // dynamite (lit 3)
4985       element = EL_EM_DYNAMITE_ACTIVE;
4986       break;
4987
4988     case 0x0f67:        // dynamite (lit 4)
4989       element = EL_EM_DYNAMITE_ACTIVE;
4990       break;
4991
4992     case 0x0f81:
4993     case 0x0f82:
4994     case 0x0f83:
4995     case 0x0f84:
4996       element = EL_AMOEBA_WET;
4997       break;
4998
4999     case 0x0f85:
5000       element = EL_AMOEBA_DROP;
5001       break;
5002
5003     case 0x0fb9:
5004       element = EL_DC_MAGIC_WALL;
5005       break;
5006
5007     case 0x0fd0:
5008       element = EL_SPACESHIP_UP;
5009       break;
5010
5011     case 0x0fd9:
5012       element = EL_SPACESHIP_DOWN;
5013       break;
5014
5015     case 0x0ff1:
5016       element = EL_SPACESHIP_LEFT;
5017       break;
5018
5019     case 0x0ff9:
5020       element = EL_SPACESHIP_RIGHT;
5021       break;
5022
5023     case 0x1057:
5024       element = EL_BUG_UP;
5025       break;
5026
5027     case 0x1060:
5028       element = EL_BUG_DOWN;
5029       break;
5030
5031     case 0x1078:
5032       element = EL_BUG_LEFT;
5033       break;
5034
5035     case 0x1080:
5036       element = EL_BUG_RIGHT;
5037       break;
5038
5039     case 0x10de:
5040       element = EL_MOLE_UP;
5041       break;
5042
5043     case 0x10e7:
5044       element = EL_MOLE_DOWN;
5045       break;
5046
5047     case 0x10ff:
5048       element = EL_MOLE_LEFT;
5049       break;
5050
5051     case 0x1107:
5052       element = EL_MOLE_RIGHT;
5053       break;
5054
5055     case 0x11c0:
5056       element = EL_ROBOT;
5057       break;
5058
5059     case 0x13f5:
5060       element = EL_YAMYAM_UP;
5061       break;
5062
5063     case 0x1425:
5064       element = EL_SWITCHGATE_OPEN;
5065       break;
5066
5067     case 0x1426:
5068       element = EL_SWITCHGATE_CLOSED;
5069       break;
5070
5071     case 0x1437:
5072       element = EL_DC_SWITCHGATE_SWITCH_UP;
5073       break;
5074
5075     case 0x143a:
5076       element = EL_TIMEGATE_CLOSED;
5077       break;
5078
5079     case 0x144c:        // conveyor belt switch (green)
5080       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5081       break;
5082
5083     case 0x144f:        // conveyor belt switch (red)
5084       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5085       break;
5086
5087     case 0x1452:        // conveyor belt switch (blue)
5088       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5089       break;
5090
5091     case 0x145b:
5092       element = EL_CONVEYOR_BELT_3_MIDDLE;
5093       break;
5094
5095     case 0x1463:
5096       element = EL_CONVEYOR_BELT_3_LEFT;
5097       break;
5098
5099     case 0x146b:
5100       element = EL_CONVEYOR_BELT_3_RIGHT;
5101       break;
5102
5103     case 0x1473:
5104       element = EL_CONVEYOR_BELT_1_MIDDLE;
5105       break;
5106
5107     case 0x147b:
5108       element = EL_CONVEYOR_BELT_1_LEFT;
5109       break;
5110
5111     case 0x1483:
5112       element = EL_CONVEYOR_BELT_1_RIGHT;
5113       break;
5114
5115     case 0x148b:
5116       element = EL_CONVEYOR_BELT_4_MIDDLE;
5117       break;
5118
5119     case 0x1493:
5120       element = EL_CONVEYOR_BELT_4_LEFT;
5121       break;
5122
5123     case 0x149b:
5124       element = EL_CONVEYOR_BELT_4_RIGHT;
5125       break;
5126
5127     case 0x14ac:
5128       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5129       break;
5130
5131     case 0x14bd:
5132       element = EL_EXPANDABLE_WALL_VERTICAL;
5133       break;
5134
5135     case 0x14c6:
5136       element = EL_EXPANDABLE_WALL_ANY;
5137       break;
5138
5139     case 0x14ce:        // growing steel wall (left/right)
5140       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5141       break;
5142
5143     case 0x14df:        // growing steel wall (up/down)
5144       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5145       break;
5146
5147     case 0x14e8:        // growing steel wall (up/down/left/right)
5148       element = EL_EXPANDABLE_STEELWALL_ANY;
5149       break;
5150
5151     case 0x14e9:
5152       element = EL_SHIELD_DEADLY;
5153       break;
5154
5155     case 0x1501:
5156       element = EL_EXTRA_TIME;
5157       break;
5158
5159     case 0x154f:
5160       element = EL_ACID;
5161       break;
5162
5163     case 0x1577:
5164       element = EL_EMPTY_SPACE;
5165       break;
5166
5167     case 0x1578:        // quicksand (empty)
5168       element = EL_QUICKSAND_FAST_EMPTY;
5169       break;
5170
5171     case 0x1579:        // slow quicksand (empty)
5172       element = EL_QUICKSAND_EMPTY;
5173       break;
5174
5175       // 0x157c - 0x158b:
5176       // EL_SAND
5177
5178       // 0x1590 - 0x159f:
5179       // EL_DC_LANDMINE
5180
5181     case 0x15a0:
5182       element = EL_EM_DYNAMITE;
5183       break;
5184
5185     case 0x15a1:        // key (red)
5186       element = EL_EM_KEY_1;
5187       break;
5188
5189     case 0x15a2:        // key (yellow)
5190       element = EL_EM_KEY_2;
5191       break;
5192
5193     case 0x15a3:        // key (blue)
5194       element = EL_EM_KEY_4;
5195       break;
5196
5197     case 0x15a4:        // key (green)
5198       element = EL_EM_KEY_3;
5199       break;
5200
5201     case 0x15a5:        // key (white)
5202       element = EL_DC_KEY_WHITE;
5203       break;
5204
5205     case 0x15a6:
5206       element = EL_WALL_SLIPPERY;
5207       break;
5208
5209     case 0x15a7:
5210       element = EL_WALL;
5211       break;
5212
5213     case 0x15a8:        // wall (not round)
5214       element = EL_WALL;
5215       break;
5216
5217     case 0x15a9:        // (blue)
5218       element = EL_CHAR_A;
5219       break;
5220
5221     case 0x15aa:        // (blue)
5222       element = EL_CHAR_B;
5223       break;
5224
5225     case 0x15ab:        // (blue)
5226       element = EL_CHAR_C;
5227       break;
5228
5229     case 0x15ac:        // (blue)
5230       element = EL_CHAR_D;
5231       break;
5232
5233     case 0x15ad:        // (blue)
5234       element = EL_CHAR_E;
5235       break;
5236
5237     case 0x15ae:        // (blue)
5238       element = EL_CHAR_F;
5239       break;
5240
5241     case 0x15af:        // (blue)
5242       element = EL_CHAR_G;
5243       break;
5244
5245     case 0x15b0:        // (blue)
5246       element = EL_CHAR_H;
5247       break;
5248
5249     case 0x15b1:        // (blue)
5250       element = EL_CHAR_I;
5251       break;
5252
5253     case 0x15b2:        // (blue)
5254       element = EL_CHAR_J;
5255       break;
5256
5257     case 0x15b3:        // (blue)
5258       element = EL_CHAR_K;
5259       break;
5260
5261     case 0x15b4:        // (blue)
5262       element = EL_CHAR_L;
5263       break;
5264
5265     case 0x15b5:        // (blue)
5266       element = EL_CHAR_M;
5267       break;
5268
5269     case 0x15b6:        // (blue)
5270       element = EL_CHAR_N;
5271       break;
5272
5273     case 0x15b7:        // (blue)
5274       element = EL_CHAR_O;
5275       break;
5276
5277     case 0x15b8:        // (blue)
5278       element = EL_CHAR_P;
5279       break;
5280
5281     case 0x15b9:        // (blue)
5282       element = EL_CHAR_Q;
5283       break;
5284
5285     case 0x15ba:        // (blue)
5286       element = EL_CHAR_R;
5287       break;
5288
5289     case 0x15bb:        // (blue)
5290       element = EL_CHAR_S;
5291       break;
5292
5293     case 0x15bc:        // (blue)
5294       element = EL_CHAR_T;
5295       break;
5296
5297     case 0x15bd:        // (blue)
5298       element = EL_CHAR_U;
5299       break;
5300
5301     case 0x15be:        // (blue)
5302       element = EL_CHAR_V;
5303       break;
5304
5305     case 0x15bf:        // (blue)
5306       element = EL_CHAR_W;
5307       break;
5308
5309     case 0x15c0:        // (blue)
5310       element = EL_CHAR_X;
5311       break;
5312
5313     case 0x15c1:        // (blue)
5314       element = EL_CHAR_Y;
5315       break;
5316
5317     case 0x15c2:        // (blue)
5318       element = EL_CHAR_Z;
5319       break;
5320
5321     case 0x15c3:        // (blue)
5322       element = EL_CHAR_AUMLAUT;
5323       break;
5324
5325     case 0x15c4:        // (blue)
5326       element = EL_CHAR_OUMLAUT;
5327       break;
5328
5329     case 0x15c5:        // (blue)
5330       element = EL_CHAR_UUMLAUT;
5331       break;
5332
5333     case 0x15c6:        // (blue)
5334       element = EL_CHAR_0;
5335       break;
5336
5337     case 0x15c7:        // (blue)
5338       element = EL_CHAR_1;
5339       break;
5340
5341     case 0x15c8:        // (blue)
5342       element = EL_CHAR_2;
5343       break;
5344
5345     case 0x15c9:        // (blue)
5346       element = EL_CHAR_3;
5347       break;
5348
5349     case 0x15ca:        // (blue)
5350       element = EL_CHAR_4;
5351       break;
5352
5353     case 0x15cb:        // (blue)
5354       element = EL_CHAR_5;
5355       break;
5356
5357     case 0x15cc:        // (blue)
5358       element = EL_CHAR_6;
5359       break;
5360
5361     case 0x15cd:        // (blue)
5362       element = EL_CHAR_7;
5363       break;
5364
5365     case 0x15ce:        // (blue)
5366       element = EL_CHAR_8;
5367       break;
5368
5369     case 0x15cf:        // (blue)
5370       element = EL_CHAR_9;
5371       break;
5372
5373     case 0x15d0:        // (blue)
5374       element = EL_CHAR_PERIOD;
5375       break;
5376
5377     case 0x15d1:        // (blue)
5378       element = EL_CHAR_EXCLAM;
5379       break;
5380
5381     case 0x15d2:        // (blue)
5382       element = EL_CHAR_COLON;
5383       break;
5384
5385     case 0x15d3:        // (blue)
5386       element = EL_CHAR_LESS;
5387       break;
5388
5389     case 0x15d4:        // (blue)
5390       element = EL_CHAR_GREATER;
5391       break;
5392
5393     case 0x15d5:        // (blue)
5394       element = EL_CHAR_QUESTION;
5395       break;
5396
5397     case 0x15d6:        // (blue)
5398       element = EL_CHAR_COPYRIGHT;
5399       break;
5400
5401     case 0x15d7:        // (blue)
5402       element = EL_CHAR_UP;
5403       break;
5404
5405     case 0x15d8:        // (blue)
5406       element = EL_CHAR_DOWN;
5407       break;
5408
5409     case 0x15d9:        // (blue)
5410       element = EL_CHAR_BUTTON;
5411       break;
5412
5413     case 0x15da:        // (blue)
5414       element = EL_CHAR_PLUS;
5415       break;
5416
5417     case 0x15db:        // (blue)
5418       element = EL_CHAR_MINUS;
5419       break;
5420
5421     case 0x15dc:        // (blue)
5422       element = EL_CHAR_APOSTROPHE;
5423       break;
5424
5425     case 0x15dd:        // (blue)
5426       element = EL_CHAR_PARENLEFT;
5427       break;
5428
5429     case 0x15de:        // (blue)
5430       element = EL_CHAR_PARENRIGHT;
5431       break;
5432
5433     case 0x15df:        // (green)
5434       element = EL_CHAR_A;
5435       break;
5436
5437     case 0x15e0:        // (green)
5438       element = EL_CHAR_B;
5439       break;
5440
5441     case 0x15e1:        // (green)
5442       element = EL_CHAR_C;
5443       break;
5444
5445     case 0x15e2:        // (green)
5446       element = EL_CHAR_D;
5447       break;
5448
5449     case 0x15e3:        // (green)
5450       element = EL_CHAR_E;
5451       break;
5452
5453     case 0x15e4:        // (green)
5454       element = EL_CHAR_F;
5455       break;
5456
5457     case 0x15e5:        // (green)
5458       element = EL_CHAR_G;
5459       break;
5460
5461     case 0x15e6:        // (green)
5462       element = EL_CHAR_H;
5463       break;
5464
5465     case 0x15e7:        // (green)
5466       element = EL_CHAR_I;
5467       break;
5468
5469     case 0x15e8:        // (green)
5470       element = EL_CHAR_J;
5471       break;
5472
5473     case 0x15e9:        // (green)
5474       element = EL_CHAR_K;
5475       break;
5476
5477     case 0x15ea:        // (green)
5478       element = EL_CHAR_L;
5479       break;
5480
5481     case 0x15eb:        // (green)
5482       element = EL_CHAR_M;
5483       break;
5484
5485     case 0x15ec:        // (green)
5486       element = EL_CHAR_N;
5487       break;
5488
5489     case 0x15ed:        // (green)
5490       element = EL_CHAR_O;
5491       break;
5492
5493     case 0x15ee:        // (green)
5494       element = EL_CHAR_P;
5495       break;
5496
5497     case 0x15ef:        // (green)
5498       element = EL_CHAR_Q;
5499       break;
5500
5501     case 0x15f0:        // (green)
5502       element = EL_CHAR_R;
5503       break;
5504
5505     case 0x15f1:        // (green)
5506       element = EL_CHAR_S;
5507       break;
5508
5509     case 0x15f2:        // (green)
5510       element = EL_CHAR_T;
5511       break;
5512
5513     case 0x15f3:        // (green)
5514       element = EL_CHAR_U;
5515       break;
5516
5517     case 0x15f4:        // (green)
5518       element = EL_CHAR_V;
5519       break;
5520
5521     case 0x15f5:        // (green)
5522       element = EL_CHAR_W;
5523       break;
5524
5525     case 0x15f6:        // (green)
5526       element = EL_CHAR_X;
5527       break;
5528
5529     case 0x15f7:        // (green)
5530       element = EL_CHAR_Y;
5531       break;
5532
5533     case 0x15f8:        // (green)
5534       element = EL_CHAR_Z;
5535       break;
5536
5537     case 0x15f9:        // (green)
5538       element = EL_CHAR_AUMLAUT;
5539       break;
5540
5541     case 0x15fa:        // (green)
5542       element = EL_CHAR_OUMLAUT;
5543       break;
5544
5545     case 0x15fb:        // (green)
5546       element = EL_CHAR_UUMLAUT;
5547       break;
5548
5549     case 0x15fc:        // (green)
5550       element = EL_CHAR_0;
5551       break;
5552
5553     case 0x15fd:        // (green)
5554       element = EL_CHAR_1;
5555       break;
5556
5557     case 0x15fe:        // (green)
5558       element = EL_CHAR_2;
5559       break;
5560
5561     case 0x15ff:        // (green)
5562       element = EL_CHAR_3;
5563       break;
5564
5565     case 0x1600:        // (green)
5566       element = EL_CHAR_4;
5567       break;
5568
5569     case 0x1601:        // (green)
5570       element = EL_CHAR_5;
5571       break;
5572
5573     case 0x1602:        // (green)
5574       element = EL_CHAR_6;
5575       break;
5576
5577     case 0x1603:        // (green)
5578       element = EL_CHAR_7;
5579       break;
5580
5581     case 0x1604:        // (green)
5582       element = EL_CHAR_8;
5583       break;
5584
5585     case 0x1605:        // (green)
5586       element = EL_CHAR_9;
5587       break;
5588
5589     case 0x1606:        // (green)
5590       element = EL_CHAR_PERIOD;
5591       break;
5592
5593     case 0x1607:        // (green)
5594       element = EL_CHAR_EXCLAM;
5595       break;
5596
5597     case 0x1608:        // (green)
5598       element = EL_CHAR_COLON;
5599       break;
5600
5601     case 0x1609:        // (green)
5602       element = EL_CHAR_LESS;
5603       break;
5604
5605     case 0x160a:        // (green)
5606       element = EL_CHAR_GREATER;
5607       break;
5608
5609     case 0x160b:        // (green)
5610       element = EL_CHAR_QUESTION;
5611       break;
5612
5613     case 0x160c:        // (green)
5614       element = EL_CHAR_COPYRIGHT;
5615       break;
5616
5617     case 0x160d:        // (green)
5618       element = EL_CHAR_UP;
5619       break;
5620
5621     case 0x160e:        // (green)
5622       element = EL_CHAR_DOWN;
5623       break;
5624
5625     case 0x160f:        // (green)
5626       element = EL_CHAR_BUTTON;
5627       break;
5628
5629     case 0x1610:        // (green)
5630       element = EL_CHAR_PLUS;
5631       break;
5632
5633     case 0x1611:        // (green)
5634       element = EL_CHAR_MINUS;
5635       break;
5636
5637     case 0x1612:        // (green)
5638       element = EL_CHAR_APOSTROPHE;
5639       break;
5640
5641     case 0x1613:        // (green)
5642       element = EL_CHAR_PARENLEFT;
5643       break;
5644
5645     case 0x1614:        // (green)
5646       element = EL_CHAR_PARENRIGHT;
5647       break;
5648
5649     case 0x1615:        // (blue steel)
5650       element = EL_STEEL_CHAR_A;
5651       break;
5652
5653     case 0x1616:        // (blue steel)
5654       element = EL_STEEL_CHAR_B;
5655       break;
5656
5657     case 0x1617:        // (blue steel)
5658       element = EL_STEEL_CHAR_C;
5659       break;
5660
5661     case 0x1618:        // (blue steel)
5662       element = EL_STEEL_CHAR_D;
5663       break;
5664
5665     case 0x1619:        // (blue steel)
5666       element = EL_STEEL_CHAR_E;
5667       break;
5668
5669     case 0x161a:        // (blue steel)
5670       element = EL_STEEL_CHAR_F;
5671       break;
5672
5673     case 0x161b:        // (blue steel)
5674       element = EL_STEEL_CHAR_G;
5675       break;
5676
5677     case 0x161c:        // (blue steel)
5678       element = EL_STEEL_CHAR_H;
5679       break;
5680
5681     case 0x161d:        // (blue steel)
5682       element = EL_STEEL_CHAR_I;
5683       break;
5684
5685     case 0x161e:        // (blue steel)
5686       element = EL_STEEL_CHAR_J;
5687       break;
5688
5689     case 0x161f:        // (blue steel)
5690       element = EL_STEEL_CHAR_K;
5691       break;
5692
5693     case 0x1620:        // (blue steel)
5694       element = EL_STEEL_CHAR_L;
5695       break;
5696
5697     case 0x1621:        // (blue steel)
5698       element = EL_STEEL_CHAR_M;
5699       break;
5700
5701     case 0x1622:        // (blue steel)
5702       element = EL_STEEL_CHAR_N;
5703       break;
5704
5705     case 0x1623:        // (blue steel)
5706       element = EL_STEEL_CHAR_O;
5707       break;
5708
5709     case 0x1624:        // (blue steel)
5710       element = EL_STEEL_CHAR_P;
5711       break;
5712
5713     case 0x1625:        // (blue steel)
5714       element = EL_STEEL_CHAR_Q;
5715       break;
5716
5717     case 0x1626:        // (blue steel)
5718       element = EL_STEEL_CHAR_R;
5719       break;
5720
5721     case 0x1627:        // (blue steel)
5722       element = EL_STEEL_CHAR_S;
5723       break;
5724
5725     case 0x1628:        // (blue steel)
5726       element = EL_STEEL_CHAR_T;
5727       break;
5728
5729     case 0x1629:        // (blue steel)
5730       element = EL_STEEL_CHAR_U;
5731       break;
5732
5733     case 0x162a:        // (blue steel)
5734       element = EL_STEEL_CHAR_V;
5735       break;
5736
5737     case 0x162b:        // (blue steel)
5738       element = EL_STEEL_CHAR_W;
5739       break;
5740
5741     case 0x162c:        // (blue steel)
5742       element = EL_STEEL_CHAR_X;
5743       break;
5744
5745     case 0x162d:        // (blue steel)
5746       element = EL_STEEL_CHAR_Y;
5747       break;
5748
5749     case 0x162e:        // (blue steel)
5750       element = EL_STEEL_CHAR_Z;
5751       break;
5752
5753     case 0x162f:        // (blue steel)
5754       element = EL_STEEL_CHAR_AUMLAUT;
5755       break;
5756
5757     case 0x1630:        // (blue steel)
5758       element = EL_STEEL_CHAR_OUMLAUT;
5759       break;
5760
5761     case 0x1631:        // (blue steel)
5762       element = EL_STEEL_CHAR_UUMLAUT;
5763       break;
5764
5765     case 0x1632:        // (blue steel)
5766       element = EL_STEEL_CHAR_0;
5767       break;
5768
5769     case 0x1633:        // (blue steel)
5770       element = EL_STEEL_CHAR_1;
5771       break;
5772
5773     case 0x1634:        // (blue steel)
5774       element = EL_STEEL_CHAR_2;
5775       break;
5776
5777     case 0x1635:        // (blue steel)
5778       element = EL_STEEL_CHAR_3;
5779       break;
5780
5781     case 0x1636:        // (blue steel)
5782       element = EL_STEEL_CHAR_4;
5783       break;
5784
5785     case 0x1637:        // (blue steel)
5786       element = EL_STEEL_CHAR_5;
5787       break;
5788
5789     case 0x1638:        // (blue steel)
5790       element = EL_STEEL_CHAR_6;
5791       break;
5792
5793     case 0x1639:        // (blue steel)
5794       element = EL_STEEL_CHAR_7;
5795       break;
5796
5797     case 0x163a:        // (blue steel)
5798       element = EL_STEEL_CHAR_8;
5799       break;
5800
5801     case 0x163b:        // (blue steel)
5802       element = EL_STEEL_CHAR_9;
5803       break;
5804
5805     case 0x163c:        // (blue steel)
5806       element = EL_STEEL_CHAR_PERIOD;
5807       break;
5808
5809     case 0x163d:        // (blue steel)
5810       element = EL_STEEL_CHAR_EXCLAM;
5811       break;
5812
5813     case 0x163e:        // (blue steel)
5814       element = EL_STEEL_CHAR_COLON;
5815       break;
5816
5817     case 0x163f:        // (blue steel)
5818       element = EL_STEEL_CHAR_LESS;
5819       break;
5820
5821     case 0x1640:        // (blue steel)
5822       element = EL_STEEL_CHAR_GREATER;
5823       break;
5824
5825     case 0x1641:        // (blue steel)
5826       element = EL_STEEL_CHAR_QUESTION;
5827       break;
5828
5829     case 0x1642:        // (blue steel)
5830       element = EL_STEEL_CHAR_COPYRIGHT;
5831       break;
5832
5833     case 0x1643:        // (blue steel)
5834       element = EL_STEEL_CHAR_UP;
5835       break;
5836
5837     case 0x1644:        // (blue steel)
5838       element = EL_STEEL_CHAR_DOWN;
5839       break;
5840
5841     case 0x1645:        // (blue steel)
5842       element = EL_STEEL_CHAR_BUTTON;
5843       break;
5844
5845     case 0x1646:        // (blue steel)
5846       element = EL_STEEL_CHAR_PLUS;
5847       break;
5848
5849     case 0x1647:        // (blue steel)
5850       element = EL_STEEL_CHAR_MINUS;
5851       break;
5852
5853     case 0x1648:        // (blue steel)
5854       element = EL_STEEL_CHAR_APOSTROPHE;
5855       break;
5856
5857     case 0x1649:        // (blue steel)
5858       element = EL_STEEL_CHAR_PARENLEFT;
5859       break;
5860
5861     case 0x164a:        // (blue steel)
5862       element = EL_STEEL_CHAR_PARENRIGHT;
5863       break;
5864
5865     case 0x164b:        // (green steel)
5866       element = EL_STEEL_CHAR_A;
5867       break;
5868
5869     case 0x164c:        // (green steel)
5870       element = EL_STEEL_CHAR_B;
5871       break;
5872
5873     case 0x164d:        // (green steel)
5874       element = EL_STEEL_CHAR_C;
5875       break;
5876
5877     case 0x164e:        // (green steel)
5878       element = EL_STEEL_CHAR_D;
5879       break;
5880
5881     case 0x164f:        // (green steel)
5882       element = EL_STEEL_CHAR_E;
5883       break;
5884
5885     case 0x1650:        // (green steel)
5886       element = EL_STEEL_CHAR_F;
5887       break;
5888
5889     case 0x1651:        // (green steel)
5890       element = EL_STEEL_CHAR_G;
5891       break;
5892
5893     case 0x1652:        // (green steel)
5894       element = EL_STEEL_CHAR_H;
5895       break;
5896
5897     case 0x1653:        // (green steel)
5898       element = EL_STEEL_CHAR_I;
5899       break;
5900
5901     case 0x1654:        // (green steel)
5902       element = EL_STEEL_CHAR_J;
5903       break;
5904
5905     case 0x1655:        // (green steel)
5906       element = EL_STEEL_CHAR_K;
5907       break;
5908
5909     case 0x1656:        // (green steel)
5910       element = EL_STEEL_CHAR_L;
5911       break;
5912
5913     case 0x1657:        // (green steel)
5914       element = EL_STEEL_CHAR_M;
5915       break;
5916
5917     case 0x1658:        // (green steel)
5918       element = EL_STEEL_CHAR_N;
5919       break;
5920
5921     case 0x1659:        // (green steel)
5922       element = EL_STEEL_CHAR_O;
5923       break;
5924
5925     case 0x165a:        // (green steel)
5926       element = EL_STEEL_CHAR_P;
5927       break;
5928
5929     case 0x165b:        // (green steel)
5930       element = EL_STEEL_CHAR_Q;
5931       break;
5932
5933     case 0x165c:        // (green steel)
5934       element = EL_STEEL_CHAR_R;
5935       break;
5936
5937     case 0x165d:        // (green steel)
5938       element = EL_STEEL_CHAR_S;
5939       break;
5940
5941     case 0x165e:        // (green steel)
5942       element = EL_STEEL_CHAR_T;
5943       break;
5944
5945     case 0x165f:        // (green steel)
5946       element = EL_STEEL_CHAR_U;
5947       break;
5948
5949     case 0x1660:        // (green steel)
5950       element = EL_STEEL_CHAR_V;
5951       break;
5952
5953     case 0x1661:        // (green steel)
5954       element = EL_STEEL_CHAR_W;
5955       break;
5956
5957     case 0x1662:        // (green steel)
5958       element = EL_STEEL_CHAR_X;
5959       break;
5960
5961     case 0x1663:        // (green steel)
5962       element = EL_STEEL_CHAR_Y;
5963       break;
5964
5965     case 0x1664:        // (green steel)
5966       element = EL_STEEL_CHAR_Z;
5967       break;
5968
5969     case 0x1665:        // (green steel)
5970       element = EL_STEEL_CHAR_AUMLAUT;
5971       break;
5972
5973     case 0x1666:        // (green steel)
5974       element = EL_STEEL_CHAR_OUMLAUT;
5975       break;
5976
5977     case 0x1667:        // (green steel)
5978       element = EL_STEEL_CHAR_UUMLAUT;
5979       break;
5980
5981     case 0x1668:        // (green steel)
5982       element = EL_STEEL_CHAR_0;
5983       break;
5984
5985     case 0x1669:        // (green steel)
5986       element = EL_STEEL_CHAR_1;
5987       break;
5988
5989     case 0x166a:        // (green steel)
5990       element = EL_STEEL_CHAR_2;
5991       break;
5992
5993     case 0x166b:        // (green steel)
5994       element = EL_STEEL_CHAR_3;
5995       break;
5996
5997     case 0x166c:        // (green steel)
5998       element = EL_STEEL_CHAR_4;
5999       break;
6000
6001     case 0x166d:        // (green steel)
6002       element = EL_STEEL_CHAR_5;
6003       break;
6004
6005     case 0x166e:        // (green steel)
6006       element = EL_STEEL_CHAR_6;
6007       break;
6008
6009     case 0x166f:        // (green steel)
6010       element = EL_STEEL_CHAR_7;
6011       break;
6012
6013     case 0x1670:        // (green steel)
6014       element = EL_STEEL_CHAR_8;
6015       break;
6016
6017     case 0x1671:        // (green steel)
6018       element = EL_STEEL_CHAR_9;
6019       break;
6020
6021     case 0x1672:        // (green steel)
6022       element = EL_STEEL_CHAR_PERIOD;
6023       break;
6024
6025     case 0x1673:        // (green steel)
6026       element = EL_STEEL_CHAR_EXCLAM;
6027       break;
6028
6029     case 0x1674:        // (green steel)
6030       element = EL_STEEL_CHAR_COLON;
6031       break;
6032
6033     case 0x1675:        // (green steel)
6034       element = EL_STEEL_CHAR_LESS;
6035       break;
6036
6037     case 0x1676:        // (green steel)
6038       element = EL_STEEL_CHAR_GREATER;
6039       break;
6040
6041     case 0x1677:        // (green steel)
6042       element = EL_STEEL_CHAR_QUESTION;
6043       break;
6044
6045     case 0x1678:        // (green steel)
6046       element = EL_STEEL_CHAR_COPYRIGHT;
6047       break;
6048
6049     case 0x1679:        // (green steel)
6050       element = EL_STEEL_CHAR_UP;
6051       break;
6052
6053     case 0x167a:        // (green steel)
6054       element = EL_STEEL_CHAR_DOWN;
6055       break;
6056
6057     case 0x167b:        // (green steel)
6058       element = EL_STEEL_CHAR_BUTTON;
6059       break;
6060
6061     case 0x167c:        // (green steel)
6062       element = EL_STEEL_CHAR_PLUS;
6063       break;
6064
6065     case 0x167d:        // (green steel)
6066       element = EL_STEEL_CHAR_MINUS;
6067       break;
6068
6069     case 0x167e:        // (green steel)
6070       element = EL_STEEL_CHAR_APOSTROPHE;
6071       break;
6072
6073     case 0x167f:        // (green steel)
6074       element = EL_STEEL_CHAR_PARENLEFT;
6075       break;
6076
6077     case 0x1680:        // (green steel)
6078       element = EL_STEEL_CHAR_PARENRIGHT;
6079       break;
6080
6081     case 0x1681:        // gate (red)
6082       element = EL_EM_GATE_1;
6083       break;
6084
6085     case 0x1682:        // secret gate (red)
6086       element = EL_EM_GATE_1_GRAY;
6087       break;
6088
6089     case 0x1683:        // gate (yellow)
6090       element = EL_EM_GATE_2;
6091       break;
6092
6093     case 0x1684:        // secret gate (yellow)
6094       element = EL_EM_GATE_2_GRAY;
6095       break;
6096
6097     case 0x1685:        // gate (blue)
6098       element = EL_EM_GATE_4;
6099       break;
6100
6101     case 0x1686:        // secret gate (blue)
6102       element = EL_EM_GATE_4_GRAY;
6103       break;
6104
6105     case 0x1687:        // gate (green)
6106       element = EL_EM_GATE_3;
6107       break;
6108
6109     case 0x1688:        // secret gate (green)
6110       element = EL_EM_GATE_3_GRAY;
6111       break;
6112
6113     case 0x1689:        // gate (white)
6114       element = EL_DC_GATE_WHITE;
6115       break;
6116
6117     case 0x168a:        // secret gate (white)
6118       element = EL_DC_GATE_WHITE_GRAY;
6119       break;
6120
6121     case 0x168b:        // secret gate (no key)
6122       element = EL_DC_GATE_FAKE_GRAY;
6123       break;
6124
6125     case 0x168c:
6126       element = EL_ROBOT_WHEEL;
6127       break;
6128
6129     case 0x168d:
6130       element = EL_DC_TIMEGATE_SWITCH;
6131       break;
6132
6133     case 0x168e:
6134       element = EL_ACID_POOL_BOTTOM;
6135       break;
6136
6137     case 0x168f:
6138       element = EL_ACID_POOL_TOPLEFT;
6139       break;
6140
6141     case 0x1690:
6142       element = EL_ACID_POOL_TOPRIGHT;
6143       break;
6144
6145     case 0x1691:
6146       element = EL_ACID_POOL_BOTTOMLEFT;
6147       break;
6148
6149     case 0x1692:
6150       element = EL_ACID_POOL_BOTTOMRIGHT;
6151       break;
6152
6153     case 0x1693:
6154       element = EL_STEELWALL;
6155       break;
6156
6157     case 0x1694:
6158       element = EL_STEELWALL_SLIPPERY;
6159       break;
6160
6161     case 0x1695:        // steel wall (not round)
6162       element = EL_STEELWALL;
6163       break;
6164
6165     case 0x1696:        // steel wall (left)
6166       element = EL_DC_STEELWALL_1_LEFT;
6167       break;
6168
6169     case 0x1697:        // steel wall (bottom)
6170       element = EL_DC_STEELWALL_1_BOTTOM;
6171       break;
6172
6173     case 0x1698:        // steel wall (right)
6174       element = EL_DC_STEELWALL_1_RIGHT;
6175       break;
6176
6177     case 0x1699:        // steel wall (top)
6178       element = EL_DC_STEELWALL_1_TOP;
6179       break;
6180
6181     case 0x169a:        // steel wall (left/bottom)
6182       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6183       break;
6184
6185     case 0x169b:        // steel wall (right/bottom)
6186       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6187       break;
6188
6189     case 0x169c:        // steel wall (right/top)
6190       element = EL_DC_STEELWALL_1_TOPRIGHT;
6191       break;
6192
6193     case 0x169d:        // steel wall (left/top)
6194       element = EL_DC_STEELWALL_1_TOPLEFT;
6195       break;
6196
6197     case 0x169e:        // steel wall (right/bottom small)
6198       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6199       break;
6200
6201     case 0x169f:        // steel wall (left/bottom small)
6202       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6203       break;
6204
6205     case 0x16a0:        // steel wall (right/top small)
6206       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6207       break;
6208
6209     case 0x16a1:        // steel wall (left/top small)
6210       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6211       break;
6212
6213     case 0x16a2:        // steel wall (left/right)
6214       element = EL_DC_STEELWALL_1_VERTICAL;
6215       break;
6216
6217     case 0x16a3:        // steel wall (top/bottom)
6218       element = EL_DC_STEELWALL_1_HORIZONTAL;
6219       break;
6220
6221     case 0x16a4:        // steel wall 2 (left end)
6222       element = EL_DC_STEELWALL_2_LEFT;
6223       break;
6224
6225     case 0x16a5:        // steel wall 2 (right end)
6226       element = EL_DC_STEELWALL_2_RIGHT;
6227       break;
6228
6229     case 0x16a6:        // steel wall 2 (top end)
6230       element = EL_DC_STEELWALL_2_TOP;
6231       break;
6232
6233     case 0x16a7:        // steel wall 2 (bottom end)
6234       element = EL_DC_STEELWALL_2_BOTTOM;
6235       break;
6236
6237     case 0x16a8:        // steel wall 2 (left/right)
6238       element = EL_DC_STEELWALL_2_HORIZONTAL;
6239       break;
6240
6241     case 0x16a9:        // steel wall 2 (up/down)
6242       element = EL_DC_STEELWALL_2_VERTICAL;
6243       break;
6244
6245     case 0x16aa:        // steel wall 2 (mid)
6246       element = EL_DC_STEELWALL_2_MIDDLE;
6247       break;
6248
6249     case 0x16ab:
6250       element = EL_SIGN_EXCLAMATION;
6251       break;
6252
6253     case 0x16ac:
6254       element = EL_SIGN_RADIOACTIVITY;
6255       break;
6256
6257     case 0x16ad:
6258       element = EL_SIGN_STOP;
6259       break;
6260
6261     case 0x16ae:
6262       element = EL_SIGN_WHEELCHAIR;
6263       break;
6264
6265     case 0x16af:
6266       element = EL_SIGN_PARKING;
6267       break;
6268
6269     case 0x16b0:
6270       element = EL_SIGN_NO_ENTRY;
6271       break;
6272
6273     case 0x16b1:
6274       element = EL_SIGN_HEART;
6275       break;
6276
6277     case 0x16b2:
6278       element = EL_SIGN_GIVE_WAY;
6279       break;
6280
6281     case 0x16b3:
6282       element = EL_SIGN_ENTRY_FORBIDDEN;
6283       break;
6284
6285     case 0x16b4:
6286       element = EL_SIGN_EMERGENCY_EXIT;
6287       break;
6288
6289     case 0x16b5:
6290       element = EL_SIGN_YIN_YANG;
6291       break;
6292
6293     case 0x16b6:
6294       element = EL_WALL_EMERALD;
6295       break;
6296
6297     case 0x16b7:
6298       element = EL_WALL_DIAMOND;
6299       break;
6300
6301     case 0x16b8:
6302       element = EL_WALL_PEARL;
6303       break;
6304
6305     case 0x16b9:
6306       element = EL_WALL_CRYSTAL;
6307       break;
6308
6309     case 0x16ba:
6310       element = EL_INVISIBLE_WALL;
6311       break;
6312
6313     case 0x16bb:
6314       element = EL_INVISIBLE_STEELWALL;
6315       break;
6316
6317       // 0x16bc - 0x16cb:
6318       // EL_INVISIBLE_SAND
6319
6320     case 0x16cc:
6321       element = EL_LIGHT_SWITCH;
6322       break;
6323
6324     case 0x16cd:
6325       element = EL_ENVELOPE_1;
6326       break;
6327
6328     default:
6329       if (element >= 0x0117 && element <= 0x036e)       // (?)
6330         element = EL_DIAMOND;
6331       else if (element >= 0x042d && element <= 0x0684)  // (?)
6332         element = EL_EMERALD;
6333       else if (element >= 0x157c && element <= 0x158b)
6334         element = EL_SAND;
6335       else if (element >= 0x1590 && element <= 0x159f)
6336         element = EL_DC_LANDMINE;
6337       else if (element >= 0x16bc && element <= 0x16cb)
6338         element = EL_INVISIBLE_SAND;
6339       else
6340       {
6341         Warn("unknown Diamond Caves element 0x%04x", element);
6342
6343         element = EL_UNKNOWN;
6344       }
6345       break;
6346   }
6347
6348   return getMappedElement(element);
6349 }
6350
6351 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6352 {
6353   byte header[DC_LEVEL_HEADER_SIZE];
6354   int envelope_size;
6355   int envelope_header_pos = 62;
6356   int envelope_content_pos = 94;
6357   int level_name_pos = 251;
6358   int level_author_pos = 292;
6359   int envelope_header_len;
6360   int envelope_content_len;
6361   int level_name_len;
6362   int level_author_len;
6363   int fieldx, fieldy;
6364   int num_yamyam_contents;
6365   int i, x, y;
6366
6367   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6368
6369   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6370   {
6371     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6372
6373     header[i * 2 + 0] = header_word >> 8;
6374     header[i * 2 + 1] = header_word & 0xff;
6375   }
6376
6377   // read some values from level header to check level decoding integrity
6378   fieldx = header[6] | (header[7] << 8);
6379   fieldy = header[8] | (header[9] << 8);
6380   num_yamyam_contents = header[60] | (header[61] << 8);
6381
6382   // do some simple sanity checks to ensure that level was correctly decoded
6383   if (fieldx < 1 || fieldx > 256 ||
6384       fieldy < 1 || fieldy > 256 ||
6385       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6386   {
6387     level->no_valid_file = TRUE;
6388
6389     Warn("cannot decode level from stream -- using empty level");
6390
6391     return;
6392   }
6393
6394   // maximum envelope header size is 31 bytes
6395   envelope_header_len   = header[envelope_header_pos];
6396   // maximum envelope content size is 110 (156?) bytes
6397   envelope_content_len  = header[envelope_content_pos];
6398
6399   // maximum level title size is 40 bytes
6400   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6401   // maximum level author size is 30 (51?) bytes
6402   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6403
6404   envelope_size = 0;
6405
6406   for (i = 0; i < envelope_header_len; i++)
6407     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6408       level->envelope[0].text[envelope_size++] =
6409         header[envelope_header_pos + 1 + i];
6410
6411   if (envelope_header_len > 0 && envelope_content_len > 0)
6412   {
6413     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6414       level->envelope[0].text[envelope_size++] = '\n';
6415     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6416       level->envelope[0].text[envelope_size++] = '\n';
6417   }
6418
6419   for (i = 0; i < envelope_content_len; i++)
6420     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6421       level->envelope[0].text[envelope_size++] =
6422         header[envelope_content_pos + 1 + i];
6423
6424   level->envelope[0].text[envelope_size] = '\0';
6425
6426   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6427   level->envelope[0].ysize = 10;
6428   level->envelope[0].autowrap = TRUE;
6429   level->envelope[0].centered = TRUE;
6430
6431   for (i = 0; i < level_name_len; i++)
6432     level->name[i] = header[level_name_pos + 1 + i];
6433   level->name[level_name_len] = '\0';
6434
6435   for (i = 0; i < level_author_len; i++)
6436     level->author[i] = header[level_author_pos + 1 + i];
6437   level->author[level_author_len] = '\0';
6438
6439   num_yamyam_contents = header[60] | (header[61] << 8);
6440   level->num_yamyam_contents =
6441     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6442
6443   for (i = 0; i < num_yamyam_contents; i++)
6444   {
6445     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6446     {
6447       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6448       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6449
6450       if (i < MAX_ELEMENT_CONTENTS)
6451         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6452     }
6453   }
6454
6455   fieldx = header[6] | (header[7] << 8);
6456   fieldy = header[8] | (header[9] << 8);
6457   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6458   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6459
6460   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6461   {
6462     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6463     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6464
6465     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6466       level->field[x][y] = getMappedElement_DC(element_dc);
6467   }
6468
6469   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6470   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6471   level->field[x][y] = EL_PLAYER_1;
6472
6473   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6474   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6475   level->field[x][y] = EL_PLAYER_2;
6476
6477   level->gems_needed            = header[18] | (header[19] << 8);
6478
6479   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6480   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6481   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6482   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6483   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6484   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6485   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6486   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6487   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6488   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6489   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6490   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6491
6492   level->time                   = header[44] | (header[45] << 8);
6493
6494   level->amoeba_speed           = header[46] | (header[47] << 8);
6495   level->time_light             = header[48] | (header[49] << 8);
6496   level->time_timegate          = header[50] | (header[51] << 8);
6497   level->time_wheel             = header[52] | (header[53] << 8);
6498   level->time_magic_wall        = header[54] | (header[55] << 8);
6499   level->extra_time             = header[56] | (header[57] << 8);
6500   level->shield_normal_time     = header[58] | (header[59] << 8);
6501
6502   // shield and extra time elements do not have a score
6503   level->score[SC_SHIELD]       = 0;
6504   level->extra_time_score       = 0;
6505
6506   // set time for normal and deadly shields to the same value
6507   level->shield_deadly_time     = level->shield_normal_time;
6508
6509   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6510   // can slip down from flat walls, like normal walls and steel walls
6511   level->em_slippery_gems = TRUE;
6512
6513   // time score is counted for each 10 seconds left in Diamond Caves levels
6514   level->time_score_base = 10;
6515 }
6516
6517 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6518                                      struct LevelFileInfo *level_file_info,
6519                                      boolean level_info_only)
6520 {
6521   char *filename = level_file_info->filename;
6522   File *file;
6523   int num_magic_bytes = 8;
6524   char magic_bytes[num_magic_bytes + 1];
6525   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6526
6527   if (!(file = openFile(filename, MODE_READ)))
6528   {
6529     level->no_valid_file = TRUE;
6530
6531     if (!level_info_only)
6532       Warn("cannot read level '%s' -- using empty level", filename);
6533
6534     return;
6535   }
6536
6537   // fseek(file, 0x0000, SEEK_SET);
6538
6539   if (level_file_info->packed)
6540   {
6541     // read "magic bytes" from start of file
6542     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6543       magic_bytes[0] = '\0';
6544
6545     // check "magic bytes" for correct file format
6546     if (!strPrefix(magic_bytes, "DC2"))
6547     {
6548       level->no_valid_file = TRUE;
6549
6550       Warn("unknown DC level file '%s' -- using empty level", filename);
6551
6552       return;
6553     }
6554
6555     if (strPrefix(magic_bytes, "DC2Win95") ||
6556         strPrefix(magic_bytes, "DC2Win98"))
6557     {
6558       int position_first_level = 0x00fa;
6559       int extra_bytes = 4;
6560       int skip_bytes;
6561
6562       // advance file stream to first level inside the level package
6563       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6564
6565       // each block of level data is followed by block of non-level data
6566       num_levels_to_skip *= 2;
6567
6568       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6569       while (num_levels_to_skip >= 0)
6570       {
6571         // advance file stream to next level inside the level package
6572         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6573         {
6574           level->no_valid_file = TRUE;
6575
6576           Warn("cannot fseek in file '%s' -- using empty level", filename);
6577
6578           return;
6579         }
6580
6581         // skip apparently unused extra bytes following each level
6582         ReadUnusedBytesFromFile(file, extra_bytes);
6583
6584         // read size of next level in level package
6585         skip_bytes = getFile32BitLE(file);
6586
6587         num_levels_to_skip--;
6588       }
6589     }
6590     else
6591     {
6592       level->no_valid_file = TRUE;
6593
6594       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6595
6596       return;
6597     }
6598   }
6599
6600   LoadLevelFromFileStream_DC(file, level);
6601
6602   closeFile(file);
6603 }
6604
6605
6606 // ----------------------------------------------------------------------------
6607 // functions for loading SB level
6608 // ----------------------------------------------------------------------------
6609
6610 int getMappedElement_SB(int element_ascii, boolean use_ces)
6611 {
6612   static struct
6613   {
6614     int ascii;
6615     int sb;
6616     int ce;
6617   }
6618   sb_element_mapping[] =
6619   {
6620     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6621     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6622     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6623     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6624     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6625     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6626     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6627     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6628
6629     { 0,   -1,                      -1          },
6630   };
6631
6632   int i;
6633
6634   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6635     if (element_ascii == sb_element_mapping[i].ascii)
6636       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6637
6638   return EL_UNDEFINED;
6639 }
6640
6641 static void SetLevelSettings_SB(struct LevelInfo *level)
6642 {
6643   // time settings
6644   level->time = 0;
6645   level->use_step_counter = TRUE;
6646
6647   // score settings
6648   level->score[SC_TIME_BONUS] = 0;
6649   level->time_score_base = 1;
6650   level->rate_time_over_score = TRUE;
6651
6652   // game settings
6653   level->auto_exit_sokoban = TRUE;
6654 }
6655
6656 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6657                                      struct LevelFileInfo *level_file_info,
6658                                      boolean level_info_only)
6659 {
6660   char *filename = level_file_info->filename;
6661   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6662   char last_comment[MAX_LINE_LEN];
6663   char level_name[MAX_LINE_LEN];
6664   char *line_ptr;
6665   File *file;
6666   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6667   boolean read_continued_line = FALSE;
6668   boolean reading_playfield = FALSE;
6669   boolean got_valid_playfield_line = FALSE;
6670   boolean invalid_playfield_char = FALSE;
6671   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6672   int file_level_nr = 0;
6673   int x = 0, y = 0;             // initialized to make compilers happy
6674
6675   last_comment[0] = '\0';
6676   level_name[0] = '\0';
6677
6678   if (!(file = openFile(filename, MODE_READ)))
6679   {
6680     level->no_valid_file = TRUE;
6681
6682     if (!level_info_only)
6683       Warn("cannot read level '%s' -- using empty level", filename);
6684
6685     return;
6686   }
6687
6688   while (!checkEndOfFile(file))
6689   {
6690     // level successfully read, but next level may follow here
6691     if (!got_valid_playfield_line && reading_playfield)
6692     {
6693       // read playfield from single level file -- skip remaining file
6694       if (!level_file_info->packed)
6695         break;
6696
6697       if (file_level_nr >= num_levels_to_skip)
6698         break;
6699
6700       file_level_nr++;
6701
6702       last_comment[0] = '\0';
6703       level_name[0] = '\0';
6704
6705       reading_playfield = FALSE;
6706     }
6707
6708     got_valid_playfield_line = FALSE;
6709
6710     // read next line of input file
6711     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6712       break;
6713
6714     // cut trailing line break (this can be newline and/or carriage return)
6715     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6716       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6717         *line_ptr = '\0';
6718
6719     // copy raw input line for later use (mainly debugging output)
6720     strcpy(line_raw, line);
6721
6722     if (read_continued_line)
6723     {
6724       // append new line to existing line, if there is enough space
6725       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6726         strcat(previous_line, line_ptr);
6727
6728       strcpy(line, previous_line);      // copy storage buffer to line
6729
6730       read_continued_line = FALSE;
6731     }
6732
6733     // if the last character is '\', continue at next line
6734     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6735     {
6736       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6737       strcpy(previous_line, line);      // copy line to storage buffer
6738
6739       read_continued_line = TRUE;
6740
6741       continue;
6742     }
6743
6744     // skip empty lines
6745     if (line[0] == '\0')
6746       continue;
6747
6748     // extract comment text from comment line
6749     if (line[0] == ';')
6750     {
6751       for (line_ptr = line; *line_ptr; line_ptr++)
6752         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6753           break;
6754
6755       strcpy(last_comment, line_ptr);
6756
6757       continue;
6758     }
6759
6760     // extract level title text from line containing level title
6761     if (line[0] == '\'')
6762     {
6763       strcpy(level_name, &line[1]);
6764
6765       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6766         level_name[strlen(level_name) - 1] = '\0';
6767
6768       continue;
6769     }
6770
6771     // skip lines containing only spaces (or empty lines)
6772     for (line_ptr = line; *line_ptr; line_ptr++)
6773       if (*line_ptr != ' ')
6774         break;
6775     if (*line_ptr == '\0')
6776       continue;
6777
6778     // at this point, we have found a line containing part of a playfield
6779
6780     got_valid_playfield_line = TRUE;
6781
6782     if (!reading_playfield)
6783     {
6784       reading_playfield = TRUE;
6785       invalid_playfield_char = FALSE;
6786
6787       for (x = 0; x < MAX_LEV_FIELDX; x++)
6788         for (y = 0; y < MAX_LEV_FIELDY; y++)
6789           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6790
6791       level->fieldx = 0;
6792       level->fieldy = 0;
6793
6794       // start with topmost tile row
6795       y = 0;
6796     }
6797
6798     // skip playfield line if larger row than allowed
6799     if (y >= MAX_LEV_FIELDY)
6800       continue;
6801
6802     // start with leftmost tile column
6803     x = 0;
6804
6805     // read playfield elements from line
6806     for (line_ptr = line; *line_ptr; line_ptr++)
6807     {
6808       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6809
6810       // stop parsing playfield line if larger column than allowed
6811       if (x >= MAX_LEV_FIELDX)
6812         break;
6813
6814       if (mapped_sb_element == EL_UNDEFINED)
6815       {
6816         invalid_playfield_char = TRUE;
6817
6818         break;
6819       }
6820
6821       level->field[x][y] = mapped_sb_element;
6822
6823       // continue with next tile column
6824       x++;
6825
6826       level->fieldx = MAX(x, level->fieldx);
6827     }
6828
6829     if (invalid_playfield_char)
6830     {
6831       // if first playfield line, treat invalid lines as comment lines
6832       if (y == 0)
6833         reading_playfield = FALSE;
6834
6835       continue;
6836     }
6837
6838     // continue with next tile row
6839     y++;
6840   }
6841
6842   closeFile(file);
6843
6844   level->fieldy = y;
6845
6846   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6847   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6848
6849   if (!reading_playfield)
6850   {
6851     level->no_valid_file = TRUE;
6852
6853     Warn("cannot read level '%s' -- using empty level", filename);
6854
6855     return;
6856   }
6857
6858   if (*level_name != '\0')
6859   {
6860     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6861     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6862   }
6863   else if (*last_comment != '\0')
6864   {
6865     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6866     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6867   }
6868   else
6869   {
6870     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6871   }
6872
6873   // set all empty fields beyond the border walls to invisible steel wall
6874   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6875   {
6876     if ((x == 0 || x == level->fieldx - 1 ||
6877          y == 0 || y == level->fieldy - 1) &&
6878         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6879       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6880                      level->field, level->fieldx, level->fieldy);
6881   }
6882
6883   // set special level settings for Sokoban levels
6884   SetLevelSettings_SB(level);
6885
6886   if (load_xsb_to_ces)
6887   {
6888     // special global settings can now be set in level template
6889     level->use_custom_template = TRUE;
6890   }
6891 }
6892
6893
6894 // -------------------------------------------------------------------------
6895 // functions for handling native levels
6896 // -------------------------------------------------------------------------
6897
6898 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6899                                      struct LevelFileInfo *level_file_info,
6900                                      boolean level_info_only)
6901 {
6902   int pos = 0;
6903
6904   // determine position of requested level inside level package
6905   if (level_file_info->packed)
6906     pos = level_file_info->nr - leveldir_current->first_level;
6907
6908   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6909     level->no_valid_file = TRUE;
6910 }
6911
6912 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6913                                      struct LevelFileInfo *level_file_info,
6914                                      boolean level_info_only)
6915 {
6916   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6917     level->no_valid_file = TRUE;
6918 }
6919
6920 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6921                                      struct LevelFileInfo *level_file_info,
6922                                      boolean level_info_only)
6923 {
6924   int pos = 0;
6925
6926   // determine position of requested level inside level package
6927   if (level_file_info->packed)
6928     pos = level_file_info->nr - leveldir_current->first_level;
6929
6930   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6931     level->no_valid_file = TRUE;
6932 }
6933
6934 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6935                                      struct LevelFileInfo *level_file_info,
6936                                      boolean level_info_only)
6937 {
6938   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6939     level->no_valid_file = TRUE;
6940 }
6941
6942 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6943 {
6944   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6945     CopyNativeLevel_RND_to_BD(level);
6946   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6947     CopyNativeLevel_RND_to_EM(level);
6948   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6949     CopyNativeLevel_RND_to_SP(level);
6950   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6951     CopyNativeLevel_RND_to_MM(level);
6952 }
6953
6954 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6955 {
6956   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6957     CopyNativeLevel_BD_to_RND(level);
6958   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6959     CopyNativeLevel_EM_to_RND(level);
6960   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6961     CopyNativeLevel_SP_to_RND(level);
6962   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6963     CopyNativeLevel_MM_to_RND(level);
6964 }
6965
6966 void SaveNativeLevel(struct LevelInfo *level)
6967 {
6968   // saving native level files only supported for some game engines
6969   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6970       level->game_engine_type != GAME_ENGINE_TYPE_SP)
6971     return;
6972
6973   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6974                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6975   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6976   char *filename = getLevelFilenameFromBasename(basename);
6977
6978   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6979     return;
6980
6981   boolean success = FALSE;
6982
6983   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6984   {
6985     CopyNativeLevel_RND_to_BD(level);
6986     // CopyNativeTape_RND_to_BD(level);
6987
6988     success = SaveNativeLevel_BD(filename);
6989   }
6990   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6991   {
6992     CopyNativeLevel_RND_to_SP(level);
6993     CopyNativeTape_RND_to_SP(level);
6994
6995     success = SaveNativeLevel_SP(filename);
6996   }
6997
6998   if (success)
6999     Request("Native level file saved!", REQ_CONFIRM);
7000   else
7001     Request("Failed to save native level file!", REQ_CONFIRM);
7002 }
7003
7004
7005 // ----------------------------------------------------------------------------
7006 // functions for loading generic level
7007 // ----------------------------------------------------------------------------
7008
7009 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7010                                   struct LevelFileInfo *level_file_info,
7011                                   boolean level_info_only)
7012 {
7013   // always start with reliable default values
7014   setLevelInfoToDefaults(level, level_info_only, TRUE);
7015
7016   switch (level_file_info->type)
7017   {
7018     case LEVEL_FILE_TYPE_RND:
7019       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7020       break;
7021
7022     case LEVEL_FILE_TYPE_BD:
7023       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7024       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7025       break;
7026
7027     case LEVEL_FILE_TYPE_EM:
7028       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7029       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7030       break;
7031
7032     case LEVEL_FILE_TYPE_SP:
7033       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7034       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7035       break;
7036
7037     case LEVEL_FILE_TYPE_MM:
7038       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7039       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7040       break;
7041
7042     case LEVEL_FILE_TYPE_DC:
7043       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7044       break;
7045
7046     case LEVEL_FILE_TYPE_SB:
7047       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7048       break;
7049
7050     default:
7051       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7052       break;
7053   }
7054
7055   // if level file is invalid, restore level structure to default values
7056   if (level->no_valid_file)
7057     setLevelInfoToDefaults(level, level_info_only, FALSE);
7058
7059   if (check_special_flags("use_native_bd_game_engine"))
7060     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7061
7062   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7063     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7064
7065   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7066     CopyNativeLevel_Native_to_RND(level);
7067 }
7068
7069 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7070 {
7071   static struct LevelFileInfo level_file_info;
7072
7073   // always start with reliable default values
7074   setFileInfoToDefaults(&level_file_info);
7075
7076   level_file_info.nr = 0;                       // unknown level number
7077   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7078
7079   setString(&level_file_info.filename, filename);
7080
7081   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7082 }
7083
7084 static void LoadLevel_InitVersion(struct LevelInfo *level)
7085 {
7086   int i, j;
7087
7088   if (leveldir_current == NULL)         // only when dumping level
7089     return;
7090
7091   // all engine modifications also valid for levels which use latest engine
7092   if (level->game_version < VERSION_IDENT(3,2,0,5))
7093   {
7094     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7095     level->time_score_base = 10;
7096   }
7097
7098   if (leveldir_current->latest_engine)
7099   {
7100     // ---------- use latest game engine --------------------------------------
7101
7102     /* For all levels which are forced to use the latest game engine version
7103        (normally all but user contributed, private and undefined levels), set
7104        the game engine version to the actual version; this allows for actual
7105        corrections in the game engine to take effect for existing, converted
7106        levels (from "classic" or other existing games) to make the emulation
7107        of the corresponding game more accurate, while (hopefully) not breaking
7108        existing levels created from other players. */
7109
7110     level->game_version = GAME_VERSION_ACTUAL;
7111
7112     /* Set special EM style gems behaviour: EM style gems slip down from
7113        normal, steel and growing wall. As this is a more fundamental change,
7114        it seems better to set the default behaviour to "off" (as it is more
7115        natural) and make it configurable in the level editor (as a property
7116        of gem style elements). Already existing converted levels (neither
7117        private nor contributed levels) are changed to the new behaviour. */
7118
7119     if (level->file_version < FILE_VERSION_2_0)
7120       level->em_slippery_gems = TRUE;
7121
7122     return;
7123   }
7124
7125   // ---------- use game engine the level was created with --------------------
7126
7127   /* For all levels which are not forced to use the latest game engine
7128      version (normally user contributed, private and undefined levels),
7129      use the version of the game engine the levels were created for.
7130
7131      Since 2.0.1, the game engine version is now directly stored
7132      in the level file (chunk "VERS"), so there is no need anymore
7133      to set the game version from the file version (except for old,
7134      pre-2.0 levels, where the game version is still taken from the
7135      file format version used to store the level -- see above). */
7136
7137   // player was faster than enemies in 1.0.0 and before
7138   if (level->file_version == FILE_VERSION_1_0)
7139     for (i = 0; i < MAX_PLAYERS; i++)
7140       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7141
7142   // default behaviour for EM style gems was "slippery" only in 2.0.1
7143   if (level->game_version == VERSION_IDENT(2,0,1,0))
7144     level->em_slippery_gems = TRUE;
7145
7146   // springs could be pushed over pits before (pre-release version) 2.2.0
7147   if (level->game_version < VERSION_IDENT(2,2,0,0))
7148     level->use_spring_bug = TRUE;
7149
7150   if (level->game_version < VERSION_IDENT(3,2,0,5))
7151   {
7152     // time orb caused limited time in endless time levels before 3.2.0-5
7153     level->use_time_orb_bug = TRUE;
7154
7155     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7156     level->block_snap_field = FALSE;
7157
7158     // extra time score was same value as time left score before 3.2.0-5
7159     level->extra_time_score = level->score[SC_TIME_BONUS];
7160   }
7161
7162   if (level->game_version < VERSION_IDENT(3,2,0,7))
7163   {
7164     // default behaviour for snapping was "not continuous" before 3.2.0-7
7165     level->continuous_snapping = FALSE;
7166   }
7167
7168   // only few elements were able to actively move into acid before 3.1.0
7169   // trigger settings did not exist before 3.1.0; set to default "any"
7170   if (level->game_version < VERSION_IDENT(3,1,0,0))
7171   {
7172     // correct "can move into acid" settings (all zero in old levels)
7173
7174     level->can_move_into_acid_bits = 0; // nothing can move into acid
7175     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7176
7177     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7178     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7179     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7180     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7181
7182     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7183       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7184
7185     // correct trigger settings (stored as zero == "none" in old levels)
7186
7187     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7188     {
7189       int element = EL_CUSTOM_START + i;
7190       struct ElementInfo *ei = &element_info[element];
7191
7192       for (j = 0; j < ei->num_change_pages; j++)
7193       {
7194         struct ElementChangeInfo *change = &ei->change_page[j];
7195
7196         change->trigger_player = CH_PLAYER_ANY;
7197         change->trigger_page = CH_PAGE_ANY;
7198       }
7199     }
7200   }
7201
7202   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7203   {
7204     int element = EL_CUSTOM_256;
7205     struct ElementInfo *ei = &element_info[element];
7206     struct ElementChangeInfo *change = &ei->change_page[0];
7207
7208     /* This is needed to fix a problem that was caused by a bugfix in function
7209        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7210        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7211        not replace walkable elements, but instead just placed the player on it,
7212        without placing the Sokoban field under the player). Unfortunately, this
7213        breaks "Snake Bite" style levels when the snake is halfway through a door
7214        that just closes (the snake head is still alive and can be moved in this
7215        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7216        player (without Sokoban element) which then gets killed as designed). */
7217
7218     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7219          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7220         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7221       change->target_element = EL_PLAYER_1;
7222   }
7223
7224   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7225   if (level->game_version < VERSION_IDENT(3,2,5,0))
7226   {
7227     /* This is needed to fix a problem that was caused by a bugfix in function
7228        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7229        corrects the behaviour when a custom element changes to another custom
7230        element with a higher element number that has change actions defined.
7231        Normally, only one change per frame is allowed for custom elements.
7232        Therefore, it is checked if a custom element already changed in the
7233        current frame; if it did, subsequent changes are suppressed.
7234        Unfortunately, this is only checked for element changes, but not for
7235        change actions, which are still executed. As the function above loops
7236        through all custom elements from lower to higher, an element change
7237        resulting in a lower CE number won't be checked again, while a target
7238        element with a higher number will also be checked, and potential change
7239        actions will get executed for this CE, too (which is wrong), while
7240        further changes are ignored (which is correct). As this bugfix breaks
7241        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7242        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7243        behaviour for existing levels and tapes that make use of this bug */
7244
7245     level->use_action_after_change_bug = TRUE;
7246   }
7247
7248   // not centering level after relocating player was default only in 3.2.3
7249   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7250     level->shifted_relocation = TRUE;
7251
7252   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7253   if (level->game_version < VERSION_IDENT(3,2,6,0))
7254     level->em_explodes_by_fire = TRUE;
7255
7256   // levels were solved by the first player entering an exit up to 4.1.0.0
7257   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7258     level->solved_by_one_player = TRUE;
7259
7260   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7261   if (level->game_version < VERSION_IDENT(4,1,1,1))
7262     level->use_life_bugs = TRUE;
7263
7264   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7265   if (level->game_version < VERSION_IDENT(4,1,1,1))
7266     level->sb_objects_needed = FALSE;
7267
7268   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7269   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7270     level->finish_dig_collect = FALSE;
7271
7272   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7273   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7274     level->keep_walkable_ce = TRUE;
7275 }
7276
7277 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7278 {
7279   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7280   int x, y;
7281
7282   // check if this level is (not) a Sokoban level
7283   for (y = 0; y < level->fieldy; y++)
7284     for (x = 0; x < level->fieldx; x++)
7285       if (!IS_SB_ELEMENT(Tile[x][y]))
7286         is_sokoban_level = FALSE;
7287
7288   if (is_sokoban_level)
7289   {
7290     // set special level settings for Sokoban levels
7291     SetLevelSettings_SB(level);
7292   }
7293 }
7294
7295 static void LoadLevel_InitSettings(struct LevelInfo *level)
7296 {
7297   // adjust level settings for (non-native) Sokoban-style levels
7298   LoadLevel_InitSettings_SB(level);
7299
7300   // rename levels with title "nameless level" or if renaming is forced
7301   if (leveldir_current->empty_level_name != NULL &&
7302       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7303        leveldir_current->force_level_name))
7304     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7305              leveldir_current->empty_level_name, level_nr);
7306 }
7307
7308 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7309 {
7310   int i, x, y;
7311
7312   // map elements that have changed in newer versions
7313   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7314                                                     level->game_version);
7315   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7316     for (x = 0; x < 3; x++)
7317       for (y = 0; y < 3; y++)
7318         level->yamyam_content[i].e[x][y] =
7319           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7320                                     level->game_version);
7321
7322 }
7323
7324 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7325 {
7326   int i, j;
7327
7328   // map custom element change events that have changed in newer versions
7329   // (these following values were accidentally changed in version 3.0.1)
7330   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7331   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7332   {
7333     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7334     {
7335       int element = EL_CUSTOM_START + i;
7336
7337       // order of checking and copying events to be mapped is important
7338       // (do not change the start and end value -- they are constant)
7339       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7340       {
7341         if (HAS_CHANGE_EVENT(element, j - 2))
7342         {
7343           SET_CHANGE_EVENT(element, j - 2, FALSE);
7344           SET_CHANGE_EVENT(element, j, TRUE);
7345         }
7346       }
7347
7348       // order of checking and copying events to be mapped is important
7349       // (do not change the start and end value -- they are constant)
7350       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7351       {
7352         if (HAS_CHANGE_EVENT(element, j - 1))
7353         {
7354           SET_CHANGE_EVENT(element, j - 1, FALSE);
7355           SET_CHANGE_EVENT(element, j, TRUE);
7356         }
7357       }
7358     }
7359   }
7360
7361   // initialize "can_change" field for old levels with only one change page
7362   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7363   {
7364     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7365     {
7366       int element = EL_CUSTOM_START + i;
7367
7368       if (CAN_CHANGE(element))
7369         element_info[element].change->can_change = TRUE;
7370     }
7371   }
7372
7373   // correct custom element values (for old levels without these options)
7374   if (level->game_version < VERSION_IDENT(3,1,1,0))
7375   {
7376     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7377     {
7378       int element = EL_CUSTOM_START + i;
7379       struct ElementInfo *ei = &element_info[element];
7380
7381       if (ei->access_direction == MV_NO_DIRECTION)
7382         ei->access_direction = MV_ALL_DIRECTIONS;
7383     }
7384   }
7385
7386   // correct custom element values (fix invalid values for all versions)
7387   if (1)
7388   {
7389     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7390     {
7391       int element = EL_CUSTOM_START + i;
7392       struct ElementInfo *ei = &element_info[element];
7393
7394       for (j = 0; j < ei->num_change_pages; j++)
7395       {
7396         struct ElementChangeInfo *change = &ei->change_page[j];
7397
7398         if (change->trigger_player == CH_PLAYER_NONE)
7399           change->trigger_player = CH_PLAYER_ANY;
7400
7401         if (change->trigger_side == CH_SIDE_NONE)
7402           change->trigger_side = CH_SIDE_ANY;
7403       }
7404     }
7405   }
7406
7407   // initialize "can_explode" field for old levels which did not store this
7408   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7409   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7410   {
7411     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7412     {
7413       int element = EL_CUSTOM_START + i;
7414
7415       if (EXPLODES_1X1_OLD(element))
7416         element_info[element].explosion_type = EXPLODES_1X1;
7417
7418       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7419                                              EXPLODES_SMASHED(element) ||
7420                                              EXPLODES_IMPACT(element)));
7421     }
7422   }
7423
7424   // correct previously hard-coded move delay values for maze runner style
7425   if (level->game_version < VERSION_IDENT(3,1,1,0))
7426   {
7427     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7428     {
7429       int element = EL_CUSTOM_START + i;
7430
7431       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7432       {
7433         // previously hard-coded and therefore ignored
7434         element_info[element].move_delay_fixed = 9;
7435         element_info[element].move_delay_random = 0;
7436       }
7437     }
7438   }
7439
7440   // set some other uninitialized values of custom elements in older levels
7441   if (level->game_version < VERSION_IDENT(3,1,0,0))
7442   {
7443     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7444     {
7445       int element = EL_CUSTOM_START + i;
7446
7447       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7448
7449       element_info[element].explosion_delay = 17;
7450       element_info[element].ignition_delay = 8;
7451     }
7452   }
7453
7454   // set mouse click change events to work for left/middle/right mouse button
7455   if (level->game_version < VERSION_IDENT(4,2,3,0))
7456   {
7457     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7458     {
7459       int element = EL_CUSTOM_START + i;
7460       struct ElementInfo *ei = &element_info[element];
7461
7462       for (j = 0; j < ei->num_change_pages; j++)
7463       {
7464         struct ElementChangeInfo *change = &ei->change_page[j];
7465
7466         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7467             change->has_event[CE_PRESSED_BY_MOUSE] ||
7468             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7469             change->has_event[CE_MOUSE_PRESSED_ON_X])
7470           change->trigger_side = CH_SIDE_ANY;
7471       }
7472     }
7473   }
7474 }
7475
7476 static void LoadLevel_InitElements(struct LevelInfo *level)
7477 {
7478   LoadLevel_InitStandardElements(level);
7479
7480   if (level->file_has_custom_elements)
7481     LoadLevel_InitCustomElements(level);
7482
7483   // initialize element properties for level editor etc.
7484   InitElementPropertiesEngine(level->game_version);
7485   InitElementPropertiesGfxElement();
7486 }
7487
7488 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7489 {
7490   int x, y;
7491
7492   // map elements that have changed in newer versions
7493   for (y = 0; y < level->fieldy; y++)
7494     for (x = 0; x < level->fieldx; x++)
7495       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7496                                                      level->game_version);
7497
7498   // clear unused playfield data (nicer if level gets resized in editor)
7499   for (x = 0; x < MAX_LEV_FIELDX; x++)
7500     for (y = 0; y < MAX_LEV_FIELDY; y++)
7501       if (x >= level->fieldx || y >= level->fieldy)
7502         level->field[x][y] = EL_EMPTY;
7503
7504   // copy elements to runtime playfield array
7505   for (x = 0; x < MAX_LEV_FIELDX; x++)
7506     for (y = 0; y < MAX_LEV_FIELDY; y++)
7507       Tile[x][y] = level->field[x][y];
7508
7509   // initialize level size variables for faster access
7510   lev_fieldx = level->fieldx;
7511   lev_fieldy = level->fieldy;
7512
7513   // determine border element for this level
7514   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7515     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7516   else
7517     SetBorderElement();
7518 }
7519
7520 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7521 {
7522   struct LevelFileInfo *level_file_info = &level->file_info;
7523
7524   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7525     CopyNativeLevel_RND_to_Native(level);
7526 }
7527
7528 static void LoadLevelTemplate_LoadAndInit(void)
7529 {
7530   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7531
7532   LoadLevel_InitVersion(&level_template);
7533   LoadLevel_InitElements(&level_template);
7534   LoadLevel_InitSettings(&level_template);
7535
7536   ActivateLevelTemplate();
7537 }
7538
7539 void LoadLevelTemplate(int nr)
7540 {
7541   if (!fileExists(getGlobalLevelTemplateFilename()))
7542   {
7543     Warn("no level template found for this level");
7544
7545     return;
7546   }
7547
7548   setLevelFileInfo(&level_template.file_info, nr);
7549
7550   LoadLevelTemplate_LoadAndInit();
7551 }
7552
7553 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7554 {
7555   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7556
7557   LoadLevelTemplate_LoadAndInit();
7558 }
7559
7560 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7561 {
7562   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7563
7564   if (level.use_custom_template)
7565   {
7566     if (network_level != NULL)
7567       LoadNetworkLevelTemplate(network_level);
7568     else
7569       LoadLevelTemplate(-1);
7570   }
7571
7572   LoadLevel_InitVersion(&level);
7573   LoadLevel_InitElements(&level);
7574   LoadLevel_InitPlayfield(&level);
7575   LoadLevel_InitSettings(&level);
7576
7577   LoadLevel_InitNativeEngines(&level);
7578 }
7579
7580 void LoadLevel(int nr)
7581 {
7582   SetLevelSetInfo(leveldir_current->identifier, nr);
7583
7584   setLevelFileInfo(&level.file_info, nr);
7585
7586   LoadLevel_LoadAndInit(NULL);
7587 }
7588
7589 void LoadLevelInfoOnly(int nr)
7590 {
7591   setLevelFileInfo(&level.file_info, nr);
7592
7593   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7594 }
7595
7596 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7597 {
7598   SetLevelSetInfo(network_level->leveldir_identifier,
7599                   network_level->file_info.nr);
7600
7601   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7602
7603   LoadLevel_LoadAndInit(network_level);
7604 }
7605
7606 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7607 {
7608   int chunk_size = 0;
7609
7610   chunk_size += putFileVersion(file, level->file_version);
7611   chunk_size += putFileVersion(file, level->game_version);
7612
7613   return chunk_size;
7614 }
7615
7616 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7617 {
7618   int chunk_size = 0;
7619
7620   chunk_size += putFile16BitBE(file, level->creation_date.year);
7621   chunk_size += putFile8Bit(file,    level->creation_date.month);
7622   chunk_size += putFile8Bit(file,    level->creation_date.day);
7623
7624   return chunk_size;
7625 }
7626
7627 #if ENABLE_HISTORIC_CHUNKS
7628 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7629 {
7630   int i, x, y;
7631
7632   putFile8Bit(file, level->fieldx);
7633   putFile8Bit(file, level->fieldy);
7634
7635   putFile16BitBE(file, level->time);
7636   putFile16BitBE(file, level->gems_needed);
7637
7638   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7639     putFile8Bit(file, level->name[i]);
7640
7641   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7642     putFile8Bit(file, level->score[i]);
7643
7644   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7645     for (y = 0; y < 3; y++)
7646       for (x = 0; x < 3; x++)
7647         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7648                            level->yamyam_content[i].e[x][y]));
7649   putFile8Bit(file, level->amoeba_speed);
7650   putFile8Bit(file, level->time_magic_wall);
7651   putFile8Bit(file, level->time_wheel);
7652   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7653                      level->amoeba_content));
7654   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7655   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7656   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7657   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7658
7659   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7660
7661   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7662   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7663   putFile32BitBE(file, level->can_move_into_acid_bits);
7664   putFile8Bit(file, level->dont_collide_with_bits);
7665
7666   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7667   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7668
7669   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7670   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7671   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7672
7673   putFile8Bit(file, level->game_engine_type);
7674
7675   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7676 }
7677 #endif
7678
7679 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7680 {
7681   int chunk_size = 0;
7682   int i;
7683
7684   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7685     chunk_size += putFile8Bit(file, level->name[i]);
7686
7687   return chunk_size;
7688 }
7689
7690 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7691 {
7692   int chunk_size = 0;
7693   int i;
7694
7695   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7696     chunk_size += putFile8Bit(file, level->author[i]);
7697
7698   return chunk_size;
7699 }
7700
7701 #if ENABLE_HISTORIC_CHUNKS
7702 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7703 {
7704   int chunk_size = 0;
7705   int x, y;
7706
7707   for (y = 0; y < level->fieldy; y++)
7708     for (x = 0; x < level->fieldx; x++)
7709       if (level->encoding_16bit_field)
7710         chunk_size += putFile16BitBE(file, level->field[x][y]);
7711       else
7712         chunk_size += putFile8Bit(file, level->field[x][y]);
7713
7714   return chunk_size;
7715 }
7716 #endif
7717
7718 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7719 {
7720   int chunk_size = 0;
7721   int x, y;
7722
7723   for (y = 0; y < level->fieldy; y++) 
7724     for (x = 0; x < level->fieldx; x++) 
7725       chunk_size += putFile16BitBE(file, level->field[x][y]);
7726
7727   return chunk_size;
7728 }
7729
7730 #if ENABLE_HISTORIC_CHUNKS
7731 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7732 {
7733   int i, x, y;
7734
7735   putFile8Bit(file, EL_YAMYAM);
7736   putFile8Bit(file, level->num_yamyam_contents);
7737   putFile8Bit(file, 0);
7738   putFile8Bit(file, 0);
7739
7740   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7741     for (y = 0; y < 3; y++)
7742       for (x = 0; x < 3; x++)
7743         if (level->encoding_16bit_field)
7744           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7745         else
7746           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7747 }
7748 #endif
7749
7750 #if ENABLE_HISTORIC_CHUNKS
7751 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7752 {
7753   int i, x, y;
7754   int num_contents, content_xsize, content_ysize;
7755   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7756
7757   if (element == EL_YAMYAM)
7758   {
7759     num_contents = level->num_yamyam_contents;
7760     content_xsize = 3;
7761     content_ysize = 3;
7762
7763     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7764       for (y = 0; y < 3; y++)
7765         for (x = 0; x < 3; x++)
7766           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7767   }
7768   else if (element == EL_BD_AMOEBA)
7769   {
7770     num_contents = 1;
7771     content_xsize = 1;
7772     content_ysize = 1;
7773
7774     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7775       for (y = 0; y < 3; y++)
7776         for (x = 0; x < 3; x++)
7777           content_array[i][x][y] = EL_EMPTY;
7778     content_array[0][0][0] = level->amoeba_content;
7779   }
7780   else
7781   {
7782     // chunk header already written -- write empty chunk data
7783     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7784
7785     Warn("cannot save content for element '%d'", element);
7786
7787     return;
7788   }
7789
7790   putFile16BitBE(file, element);
7791   putFile8Bit(file, num_contents);
7792   putFile8Bit(file, content_xsize);
7793   putFile8Bit(file, content_ysize);
7794
7795   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7796
7797   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7798     for (y = 0; y < 3; y++)
7799       for (x = 0; x < 3; x++)
7800         putFile16BitBE(file, content_array[i][x][y]);
7801 }
7802 #endif
7803
7804 #if ENABLE_HISTORIC_CHUNKS
7805 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7806 {
7807   int envelope_nr = element - EL_ENVELOPE_1;
7808   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7809   int chunk_size = 0;
7810   int i;
7811
7812   chunk_size += putFile16BitBE(file, element);
7813   chunk_size += putFile16BitBE(file, envelope_len);
7814   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7815   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7816
7817   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7818   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7819
7820   for (i = 0; i < envelope_len; i++)
7821     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7822
7823   return chunk_size;
7824 }
7825 #endif
7826
7827 #if ENABLE_HISTORIC_CHUNKS
7828 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7829                            int num_changed_custom_elements)
7830 {
7831   int i, check = 0;
7832
7833   putFile16BitBE(file, num_changed_custom_elements);
7834
7835   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7836   {
7837     int element = EL_CUSTOM_START + i;
7838
7839     struct ElementInfo *ei = &element_info[element];
7840
7841     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7842     {
7843       if (check < num_changed_custom_elements)
7844       {
7845         putFile16BitBE(file, element);
7846         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7847       }
7848
7849       check++;
7850     }
7851   }
7852
7853   if (check != num_changed_custom_elements)     // should not happen
7854     Warn("inconsistent number of custom element properties");
7855 }
7856 #endif
7857
7858 #if ENABLE_HISTORIC_CHUNKS
7859 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7860                            int num_changed_custom_elements)
7861 {
7862   int i, check = 0;
7863
7864   putFile16BitBE(file, num_changed_custom_elements);
7865
7866   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7867   {
7868     int element = EL_CUSTOM_START + i;
7869
7870     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7871     {
7872       if (check < num_changed_custom_elements)
7873       {
7874         putFile16BitBE(file, element);
7875         putFile16BitBE(file, element_info[element].change->target_element);
7876       }
7877
7878       check++;
7879     }
7880   }
7881
7882   if (check != num_changed_custom_elements)     // should not happen
7883     Warn("inconsistent number of custom target elements");
7884 }
7885 #endif
7886
7887 #if ENABLE_HISTORIC_CHUNKS
7888 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7889                            int num_changed_custom_elements)
7890 {
7891   int i, j, x, y, check = 0;
7892
7893   putFile16BitBE(file, num_changed_custom_elements);
7894
7895   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7896   {
7897     int element = EL_CUSTOM_START + i;
7898     struct ElementInfo *ei = &element_info[element];
7899
7900     if (ei->modified_settings)
7901     {
7902       if (check < num_changed_custom_elements)
7903       {
7904         putFile16BitBE(file, element);
7905
7906         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7907           putFile8Bit(file, ei->description[j]);
7908
7909         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7910
7911         // some free bytes for future properties and padding
7912         WriteUnusedBytesToFile(file, 7);
7913
7914         putFile8Bit(file, ei->use_gfx_element);
7915         putFile16BitBE(file, ei->gfx_element_initial);
7916
7917         putFile8Bit(file, ei->collect_score_initial);
7918         putFile8Bit(file, ei->collect_count_initial);
7919
7920         putFile16BitBE(file, ei->push_delay_fixed);
7921         putFile16BitBE(file, ei->push_delay_random);
7922         putFile16BitBE(file, ei->move_delay_fixed);
7923         putFile16BitBE(file, ei->move_delay_random);
7924
7925         putFile16BitBE(file, ei->move_pattern);
7926         putFile8Bit(file, ei->move_direction_initial);
7927         putFile8Bit(file, ei->move_stepsize);
7928
7929         for (y = 0; y < 3; y++)
7930           for (x = 0; x < 3; x++)
7931             putFile16BitBE(file, ei->content.e[x][y]);
7932
7933         putFile32BitBE(file, ei->change->events);
7934
7935         putFile16BitBE(file, ei->change->target_element);
7936
7937         putFile16BitBE(file, ei->change->delay_fixed);
7938         putFile16BitBE(file, ei->change->delay_random);
7939         putFile16BitBE(file, ei->change->delay_frames);
7940
7941         putFile16BitBE(file, ei->change->initial_trigger_element);
7942
7943         putFile8Bit(file, ei->change->explode);
7944         putFile8Bit(file, ei->change->use_target_content);
7945         putFile8Bit(file, ei->change->only_if_complete);
7946         putFile8Bit(file, ei->change->use_random_replace);
7947
7948         putFile8Bit(file, ei->change->random_percentage);
7949         putFile8Bit(file, ei->change->replace_when);
7950
7951         for (y = 0; y < 3; y++)
7952           for (x = 0; x < 3; x++)
7953             putFile16BitBE(file, ei->change->content.e[x][y]);
7954
7955         putFile8Bit(file, ei->slippery_type);
7956
7957         // some free bytes for future properties and padding
7958         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7959       }
7960
7961       check++;
7962     }
7963   }
7964
7965   if (check != num_changed_custom_elements)     // should not happen
7966     Warn("inconsistent number of custom element properties");
7967 }
7968 #endif
7969
7970 #if ENABLE_HISTORIC_CHUNKS
7971 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7972 {
7973   struct ElementInfo *ei = &element_info[element];
7974   int i, j, x, y;
7975
7976   // ---------- custom element base property values (96 bytes) ----------------
7977
7978   putFile16BitBE(file, element);
7979
7980   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7981     putFile8Bit(file, ei->description[i]);
7982
7983   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7984
7985   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
7986
7987   putFile8Bit(file, ei->num_change_pages);
7988
7989   putFile16BitBE(file, ei->ce_value_fixed_initial);
7990   putFile16BitBE(file, ei->ce_value_random_initial);
7991   putFile8Bit(file, ei->use_last_ce_value);
7992
7993   putFile8Bit(file, ei->use_gfx_element);
7994   putFile16BitBE(file, ei->gfx_element_initial);
7995
7996   putFile8Bit(file, ei->collect_score_initial);
7997   putFile8Bit(file, ei->collect_count_initial);
7998
7999   putFile8Bit(file, ei->drop_delay_fixed);
8000   putFile8Bit(file, ei->push_delay_fixed);
8001   putFile8Bit(file, ei->drop_delay_random);
8002   putFile8Bit(file, ei->push_delay_random);
8003   putFile16BitBE(file, ei->move_delay_fixed);
8004   putFile16BitBE(file, ei->move_delay_random);
8005
8006   // bits 0 - 15 of "move_pattern" ...
8007   putFile16BitBE(file, ei->move_pattern & 0xffff);
8008   putFile8Bit(file, ei->move_direction_initial);
8009   putFile8Bit(file, ei->move_stepsize);
8010
8011   putFile8Bit(file, ei->slippery_type);
8012
8013   for (y = 0; y < 3; y++)
8014     for (x = 0; x < 3; x++)
8015       putFile16BitBE(file, ei->content.e[x][y]);
8016
8017   putFile16BitBE(file, ei->move_enter_element);
8018   putFile16BitBE(file, ei->move_leave_element);
8019   putFile8Bit(file, ei->move_leave_type);
8020
8021   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8022   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8023
8024   putFile8Bit(file, ei->access_direction);
8025
8026   putFile8Bit(file, ei->explosion_delay);
8027   putFile8Bit(file, ei->ignition_delay);
8028   putFile8Bit(file, ei->explosion_type);
8029
8030   // some free bytes for future custom property values and padding
8031   WriteUnusedBytesToFile(file, 1);
8032
8033   // ---------- change page property values (48 bytes) ------------------------
8034
8035   for (i = 0; i < ei->num_change_pages; i++)
8036   {
8037     struct ElementChangeInfo *change = &ei->change_page[i];
8038     unsigned int event_bits;
8039
8040     // bits 0 - 31 of "has_event[]" ...
8041     event_bits = 0;
8042     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8043       if (change->has_event[j])
8044         event_bits |= (1u << j);
8045     putFile32BitBE(file, event_bits);
8046
8047     putFile16BitBE(file, change->target_element);
8048
8049     putFile16BitBE(file, change->delay_fixed);
8050     putFile16BitBE(file, change->delay_random);
8051     putFile16BitBE(file, change->delay_frames);
8052
8053     putFile16BitBE(file, change->initial_trigger_element);
8054
8055     putFile8Bit(file, change->explode);
8056     putFile8Bit(file, change->use_target_content);
8057     putFile8Bit(file, change->only_if_complete);
8058     putFile8Bit(file, change->use_random_replace);
8059
8060     putFile8Bit(file, change->random_percentage);
8061     putFile8Bit(file, change->replace_when);
8062
8063     for (y = 0; y < 3; y++)
8064       for (x = 0; x < 3; x++)
8065         putFile16BitBE(file, change->target_content.e[x][y]);
8066
8067     putFile8Bit(file, change->can_change);
8068
8069     putFile8Bit(file, change->trigger_side);
8070
8071     putFile8Bit(file, change->trigger_player);
8072     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8073                        log_2(change->trigger_page)));
8074
8075     putFile8Bit(file, change->has_action);
8076     putFile8Bit(file, change->action_type);
8077     putFile8Bit(file, change->action_mode);
8078     putFile16BitBE(file, change->action_arg);
8079
8080     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8081     event_bits = 0;
8082     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8083       if (change->has_event[j])
8084         event_bits |= (1u << (j - 32));
8085     putFile8Bit(file, event_bits);
8086   }
8087 }
8088 #endif
8089
8090 #if ENABLE_HISTORIC_CHUNKS
8091 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8092 {
8093   struct ElementInfo *ei = &element_info[element];
8094   struct ElementGroupInfo *group = ei->group;
8095   int i;
8096
8097   putFile16BitBE(file, element);
8098
8099   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8100     putFile8Bit(file, ei->description[i]);
8101
8102   putFile8Bit(file, group->num_elements);
8103
8104   putFile8Bit(file, ei->use_gfx_element);
8105   putFile16BitBE(file, ei->gfx_element_initial);
8106
8107   putFile8Bit(file, group->choice_mode);
8108
8109   // some free bytes for future values and padding
8110   WriteUnusedBytesToFile(file, 3);
8111
8112   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8113     putFile16BitBE(file, group->element[i]);
8114 }
8115 #endif
8116
8117 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8118                                 boolean write_element)
8119 {
8120   int save_type = entry->save_type;
8121   int data_type = entry->data_type;
8122   int conf_type = entry->conf_type;
8123   int byte_mask = conf_type & CONF_MASK_BYTES;
8124   int element = entry->element;
8125   int default_value = entry->default_value;
8126   int num_bytes = 0;
8127   boolean modified = FALSE;
8128
8129   if (byte_mask != CONF_MASK_MULTI_BYTES)
8130   {
8131     void *value_ptr = entry->value;
8132     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8133                  *(int *)value_ptr);
8134
8135     // check if any settings have been modified before saving them
8136     if (value != default_value)
8137       modified = TRUE;
8138
8139     // do not save if explicitly told or if unmodified default settings
8140     if ((save_type == SAVE_CONF_NEVER) ||
8141         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8142       return 0;
8143
8144     if (write_element)
8145       num_bytes += putFile16BitBE(file, element);
8146
8147     num_bytes += putFile8Bit(file, conf_type);
8148     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8149                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8150                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8151                   0);
8152   }
8153   else if (data_type == TYPE_STRING)
8154   {
8155     char *default_string = entry->default_string;
8156     char *string = (char *)(entry->value);
8157     int string_length = strlen(string);
8158     int i;
8159
8160     // check if any settings have been modified before saving them
8161     if (!strEqual(string, default_string))
8162       modified = TRUE;
8163
8164     // do not save if explicitly told or if unmodified default settings
8165     if ((save_type == SAVE_CONF_NEVER) ||
8166         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8167       return 0;
8168
8169     if (write_element)
8170       num_bytes += putFile16BitBE(file, element);
8171
8172     num_bytes += putFile8Bit(file, conf_type);
8173     num_bytes += putFile16BitBE(file, string_length);
8174
8175     for (i = 0; i < string_length; i++)
8176       num_bytes += putFile8Bit(file, string[i]);
8177   }
8178   else if (data_type == TYPE_ELEMENT_LIST)
8179   {
8180     int *element_array = (int *)(entry->value);
8181     int num_elements = *(int *)(entry->num_entities);
8182     int i;
8183
8184     // check if any settings have been modified before saving them
8185     for (i = 0; i < num_elements; i++)
8186       if (element_array[i] != default_value)
8187         modified = TRUE;
8188
8189     // do not save if explicitly told or if unmodified default settings
8190     if ((save_type == SAVE_CONF_NEVER) ||
8191         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8192       return 0;
8193
8194     if (write_element)
8195       num_bytes += putFile16BitBE(file, element);
8196
8197     num_bytes += putFile8Bit(file, conf_type);
8198     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8199
8200     for (i = 0; i < num_elements; i++)
8201       num_bytes += putFile16BitBE(file, element_array[i]);
8202   }
8203   else if (data_type == TYPE_CONTENT_LIST)
8204   {
8205     struct Content *content = (struct Content *)(entry->value);
8206     int num_contents = *(int *)(entry->num_entities);
8207     int i, x, y;
8208
8209     // check if any settings have been modified before saving them
8210     for (i = 0; i < num_contents; i++)
8211       for (y = 0; y < 3; y++)
8212         for (x = 0; x < 3; x++)
8213           if (content[i].e[x][y] != default_value)
8214             modified = TRUE;
8215
8216     // do not save if explicitly told or if unmodified default settings
8217     if ((save_type == SAVE_CONF_NEVER) ||
8218         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8219       return 0;
8220
8221     if (write_element)
8222       num_bytes += putFile16BitBE(file, element);
8223
8224     num_bytes += putFile8Bit(file, conf_type);
8225     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8226
8227     for (i = 0; i < num_contents; i++)
8228       for (y = 0; y < 3; y++)
8229         for (x = 0; x < 3; x++)
8230           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8231   }
8232
8233   return num_bytes;
8234 }
8235
8236 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8237 {
8238   int chunk_size = 0;
8239   int i;
8240
8241   li = *level;          // copy level data into temporary buffer
8242
8243   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8244     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8245
8246   return chunk_size;
8247 }
8248
8249 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8250 {
8251   int chunk_size = 0;
8252   int i;
8253
8254   li = *level;          // copy level data into temporary buffer
8255
8256   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8257     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8258
8259   return chunk_size;
8260 }
8261
8262 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8263 {
8264   int envelope_nr = element - EL_ENVELOPE_1;
8265   int chunk_size = 0;
8266   int i;
8267
8268   chunk_size += putFile16BitBE(file, element);
8269
8270   // copy envelope data into temporary buffer
8271   xx_envelope = level->envelope[envelope_nr];
8272
8273   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8274     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8275
8276   return chunk_size;
8277 }
8278
8279 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8280 {
8281   struct ElementInfo *ei = &element_info[element];
8282   int chunk_size = 0;
8283   int i, j;
8284
8285   chunk_size += putFile16BitBE(file, element);
8286
8287   xx_ei = *ei;          // copy element data into temporary buffer
8288
8289   // set default description string for this specific element
8290   strcpy(xx_default_description, getDefaultElementDescription(ei));
8291
8292   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8293     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8294
8295   for (i = 0; i < ei->num_change_pages; i++)
8296   {
8297     struct ElementChangeInfo *change = &ei->change_page[i];
8298
8299     xx_current_change_page = i;
8300
8301     xx_change = *change;        // copy change data into temporary buffer
8302
8303     resetEventBits();
8304     setEventBitsFromEventFlags(change);
8305
8306     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8307       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8308                                          FALSE);
8309   }
8310
8311   return chunk_size;
8312 }
8313
8314 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8315 {
8316   struct ElementInfo *ei = &element_info[element];
8317   struct ElementGroupInfo *group = ei->group;
8318   int chunk_size = 0;
8319   int i;
8320
8321   chunk_size += putFile16BitBE(file, element);
8322
8323   xx_ei = *ei;          // copy element data into temporary buffer
8324   xx_group = *group;    // copy group data into temporary buffer
8325
8326   // set default description string for this specific element
8327   strcpy(xx_default_description, getDefaultElementDescription(ei));
8328
8329   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8330     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8331
8332   return chunk_size;
8333 }
8334
8335 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8336 {
8337   struct ElementInfo *ei = &element_info[element];
8338   int chunk_size = 0;
8339   int i;
8340
8341   chunk_size += putFile16BitBE(file, element);
8342
8343   xx_ei = *ei;          // copy element data into temporary buffer
8344
8345   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8346     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8347
8348   return chunk_size;
8349 }
8350
8351 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8352                                   boolean save_as_template)
8353 {
8354   int chunk_size;
8355   int i;
8356   FILE *file;
8357
8358   if (!(file = fopen(filename, MODE_WRITE)))
8359   {
8360     Warn("cannot save level file '%s'", filename);
8361
8362     return;
8363   }
8364
8365   level->file_version = FILE_VERSION_ACTUAL;
8366   level->game_version = GAME_VERSION_ACTUAL;
8367
8368   level->creation_date = getCurrentDate();
8369
8370   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8371   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8372
8373   chunk_size = SaveLevel_VERS(NULL, level);
8374   putFileChunkBE(file, "VERS", chunk_size);
8375   SaveLevel_VERS(file, level);
8376
8377   chunk_size = SaveLevel_DATE(NULL, level);
8378   putFileChunkBE(file, "DATE", chunk_size);
8379   SaveLevel_DATE(file, level);
8380
8381   chunk_size = SaveLevel_NAME(NULL, level);
8382   putFileChunkBE(file, "NAME", chunk_size);
8383   SaveLevel_NAME(file, level);
8384
8385   chunk_size = SaveLevel_AUTH(NULL, level);
8386   putFileChunkBE(file, "AUTH", chunk_size);
8387   SaveLevel_AUTH(file, level);
8388
8389   chunk_size = SaveLevel_INFO(NULL, level);
8390   putFileChunkBE(file, "INFO", chunk_size);
8391   SaveLevel_INFO(file, level);
8392
8393   chunk_size = SaveLevel_BODY(NULL, level);
8394   putFileChunkBE(file, "BODY", chunk_size);
8395   SaveLevel_BODY(file, level);
8396
8397   chunk_size = SaveLevel_ELEM(NULL, level);
8398   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8399   {
8400     putFileChunkBE(file, "ELEM", chunk_size);
8401     SaveLevel_ELEM(file, level);
8402   }
8403
8404   for (i = 0; i < NUM_ENVELOPES; i++)
8405   {
8406     int element = EL_ENVELOPE_1 + i;
8407
8408     chunk_size = SaveLevel_NOTE(NULL, level, element);
8409     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8410     {
8411       putFileChunkBE(file, "NOTE", chunk_size);
8412       SaveLevel_NOTE(file, level, element);
8413     }
8414   }
8415
8416   // if not using template level, check for non-default custom/group elements
8417   if (!level->use_custom_template || save_as_template)
8418   {
8419     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8420     {
8421       int element = EL_CUSTOM_START + i;
8422
8423       chunk_size = SaveLevel_CUSX(NULL, level, element);
8424       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8425       {
8426         putFileChunkBE(file, "CUSX", chunk_size);
8427         SaveLevel_CUSX(file, level, element);
8428       }
8429     }
8430
8431     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8432     {
8433       int element = EL_GROUP_START + i;
8434
8435       chunk_size = SaveLevel_GRPX(NULL, level, element);
8436       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8437       {
8438         putFileChunkBE(file, "GRPX", chunk_size);
8439         SaveLevel_GRPX(file, level, element);
8440       }
8441     }
8442
8443     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8444     {
8445       int element = GET_EMPTY_ELEMENT(i);
8446
8447       chunk_size = SaveLevel_EMPX(NULL, level, element);
8448       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8449       {
8450         putFileChunkBE(file, "EMPX", chunk_size);
8451         SaveLevel_EMPX(file, level, element);
8452       }
8453     }
8454   }
8455
8456   fclose(file);
8457
8458   SetFilePermissions(filename, PERMS_PRIVATE);
8459 }
8460
8461 void SaveLevel(int nr)
8462 {
8463   char *filename = getDefaultLevelFilename(nr);
8464
8465   SaveLevelFromFilename(&level, filename, FALSE);
8466 }
8467
8468 void SaveLevelTemplate(void)
8469 {
8470   char *filename = getLocalLevelTemplateFilename();
8471
8472   SaveLevelFromFilename(&level, filename, TRUE);
8473 }
8474
8475 boolean SaveLevelChecked(int nr)
8476 {
8477   char *filename = getDefaultLevelFilename(nr);
8478   boolean new_level = !fileExists(filename);
8479   boolean level_saved = FALSE;
8480
8481   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8482   {
8483     SaveLevel(nr);
8484
8485     if (new_level)
8486       Request("Level saved!", REQ_CONFIRM);
8487
8488     level_saved = TRUE;
8489   }
8490
8491   return level_saved;
8492 }
8493
8494 void DumpLevel(struct LevelInfo *level)
8495 {
8496   if (level->no_level_file || level->no_valid_file)
8497   {
8498     Warn("cannot dump -- no valid level file found");
8499
8500     return;
8501   }
8502
8503   PrintLine("-", 79);
8504   Print("Level xxx (file version %08d, game version %08d)\n",
8505         level->file_version, level->game_version);
8506   PrintLine("-", 79);
8507
8508   Print("Level author: '%s'\n", level->author);
8509   Print("Level title:  '%s'\n", level->name);
8510   Print("\n");
8511   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8512   Print("\n");
8513   Print("Level time:  %d seconds\n", level->time);
8514   Print("Gems needed: %d\n", level->gems_needed);
8515   Print("\n");
8516   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8517   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8518   Print("Time for light:      %d seconds\n", level->time_light);
8519   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8520   Print("\n");
8521   Print("Amoeba speed: %d\n", level->amoeba_speed);
8522   Print("\n");
8523
8524   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8525   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8526   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8527   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8528   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8529   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8530
8531   if (options.debug)
8532   {
8533     int i, j;
8534
8535     for (i = 0; i < NUM_ENVELOPES; i++)
8536     {
8537       char *text = level->envelope[i].text;
8538       int text_len = strlen(text);
8539       boolean has_text = FALSE;
8540
8541       for (j = 0; j < text_len; j++)
8542         if (text[j] != ' ' && text[j] != '\n')
8543           has_text = TRUE;
8544
8545       if (has_text)
8546       {
8547         Print("\n");
8548         Print("Envelope %d:\n'%s'\n", i + 1, text);
8549       }
8550     }
8551   }
8552
8553   PrintLine("-", 79);
8554 }
8555
8556 void DumpLevels(void)
8557 {
8558   static LevelDirTree *dumplevel_leveldir = NULL;
8559
8560   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8561                                                  global.dumplevel_leveldir);
8562
8563   if (dumplevel_leveldir == NULL)
8564     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8565
8566   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8567       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8568     Fail("no such level number: %d", global.dumplevel_level_nr);
8569
8570   leveldir_current = dumplevel_leveldir;
8571
8572   LoadLevel(global.dumplevel_level_nr);
8573   DumpLevel(&level);
8574
8575   CloseAllAndExit(0);
8576 }
8577
8578
8579 // ============================================================================
8580 // tape file functions
8581 // ============================================================================
8582
8583 static void setTapeInfoToDefaults(void)
8584 {
8585   int i;
8586
8587   // always start with reliable default values (empty tape)
8588   TapeErase();
8589
8590   // default values (also for pre-1.2 tapes) with only the first player
8591   tape.player_participates[0] = TRUE;
8592   for (i = 1; i < MAX_PLAYERS; i++)
8593     tape.player_participates[i] = FALSE;
8594
8595   // at least one (default: the first) player participates in every tape
8596   tape.num_participating_players = 1;
8597
8598   tape.property_bits = TAPE_PROPERTY_NONE;
8599
8600   tape.level_nr = level_nr;
8601   tape.counter = 0;
8602   tape.changed = FALSE;
8603   tape.solved = FALSE;
8604
8605   tape.recording = FALSE;
8606   tape.playing = FALSE;
8607   tape.pausing = FALSE;
8608
8609   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8610   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8611
8612   tape.no_info_chunk = TRUE;
8613   tape.no_valid_file = FALSE;
8614 }
8615
8616 static int getTapePosSize(struct TapeInfo *tape)
8617 {
8618   int tape_pos_size = 0;
8619
8620   if (tape->use_key_actions)
8621     tape_pos_size += tape->num_participating_players;
8622
8623   if (tape->use_mouse_actions)
8624     tape_pos_size += 3;         // x and y position and mouse button mask
8625
8626   tape_pos_size += 1;           // tape action delay value
8627
8628   return tape_pos_size;
8629 }
8630
8631 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8632 {
8633   tape->use_key_actions = FALSE;
8634   tape->use_mouse_actions = FALSE;
8635
8636   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8637     tape->use_key_actions = TRUE;
8638
8639   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8640     tape->use_mouse_actions = TRUE;
8641 }
8642
8643 static int getTapeActionValue(struct TapeInfo *tape)
8644 {
8645   return (tape->use_key_actions &&
8646           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8647           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8648           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8649           TAPE_ACTIONS_DEFAULT);
8650 }
8651
8652 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8653 {
8654   tape->file_version = getFileVersion(file);
8655   tape->game_version = getFileVersion(file);
8656
8657   return chunk_size;
8658 }
8659
8660 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8661 {
8662   int i;
8663
8664   tape->random_seed = getFile32BitBE(file);
8665   tape->date        = getFile32BitBE(file);
8666   tape->length      = getFile32BitBE(file);
8667
8668   // read header fields that are new since version 1.2
8669   if (tape->file_version >= FILE_VERSION_1_2)
8670   {
8671     byte store_participating_players = getFile8Bit(file);
8672     int engine_version;
8673
8674     // since version 1.2, tapes store which players participate in the tape
8675     tape->num_participating_players = 0;
8676     for (i = 0; i < MAX_PLAYERS; i++)
8677     {
8678       tape->player_participates[i] = FALSE;
8679
8680       if (store_participating_players & (1 << i))
8681       {
8682         tape->player_participates[i] = TRUE;
8683         tape->num_participating_players++;
8684       }
8685     }
8686
8687     setTapeActionFlags(tape, getFile8Bit(file));
8688
8689     tape->property_bits = getFile8Bit(file);
8690     tape->solved = getFile8Bit(file);
8691
8692     engine_version = getFileVersion(file);
8693     if (engine_version > 0)
8694       tape->engine_version = engine_version;
8695     else
8696       tape->engine_version = tape->game_version;
8697   }
8698
8699   return chunk_size;
8700 }
8701
8702 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8703 {
8704   tape->scr_fieldx = getFile8Bit(file);
8705   tape->scr_fieldy = getFile8Bit(file);
8706
8707   return chunk_size;
8708 }
8709
8710 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8711 {
8712   char *level_identifier = NULL;
8713   int level_identifier_size;
8714   int i;
8715
8716   tape->no_info_chunk = FALSE;
8717
8718   level_identifier_size = getFile16BitBE(file);
8719
8720   level_identifier = checked_malloc(level_identifier_size);
8721
8722   for (i = 0; i < level_identifier_size; i++)
8723     level_identifier[i] = getFile8Bit(file);
8724
8725   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8726   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8727
8728   checked_free(level_identifier);
8729
8730   tape->level_nr = getFile16BitBE(file);
8731
8732   chunk_size = 2 + level_identifier_size + 2;
8733
8734   return chunk_size;
8735 }
8736
8737 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8738 {
8739   int i, j;
8740   int tape_pos_size = getTapePosSize(tape);
8741   int chunk_size_expected = tape_pos_size * tape->length;
8742
8743   if (chunk_size_expected != chunk_size)
8744   {
8745     ReadUnusedBytesFromFile(file, chunk_size);
8746     return chunk_size_expected;
8747   }
8748
8749   for (i = 0; i < tape->length; i++)
8750   {
8751     if (i >= MAX_TAPE_LEN)
8752     {
8753       Warn("tape truncated -- size exceeds maximum tape size %d",
8754             MAX_TAPE_LEN);
8755
8756       // tape too large; read and ignore remaining tape data from this chunk
8757       for (;i < tape->length; i++)
8758         ReadUnusedBytesFromFile(file, tape_pos_size);
8759
8760       break;
8761     }
8762
8763     if (tape->use_key_actions)
8764     {
8765       for (j = 0; j < MAX_PLAYERS; j++)
8766       {
8767         tape->pos[i].action[j] = MV_NONE;
8768
8769         if (tape->player_participates[j])
8770           tape->pos[i].action[j] = getFile8Bit(file);
8771       }
8772     }
8773
8774     if (tape->use_mouse_actions)
8775     {
8776       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8777       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8778       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8779     }
8780
8781     tape->pos[i].delay = getFile8Bit(file);
8782
8783     if (tape->file_version == FILE_VERSION_1_0)
8784     {
8785       // eliminate possible diagonal moves in old tapes
8786       // this is only for backward compatibility
8787
8788       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8789       byte action = tape->pos[i].action[0];
8790       int k, num_moves = 0;
8791
8792       for (k = 0; k < 4; k++)
8793       {
8794         if (action & joy_dir[k])
8795         {
8796           tape->pos[i + num_moves].action[0] = joy_dir[k];
8797           if (num_moves > 0)
8798             tape->pos[i + num_moves].delay = 0;
8799           num_moves++;
8800         }
8801       }
8802
8803       if (num_moves > 1)
8804       {
8805         num_moves--;
8806         i += num_moves;
8807         tape->length += num_moves;
8808       }
8809     }
8810     else if (tape->file_version < FILE_VERSION_2_0)
8811     {
8812       // convert pre-2.0 tapes to new tape format
8813
8814       if (tape->pos[i].delay > 1)
8815       {
8816         // action part
8817         tape->pos[i + 1] = tape->pos[i];
8818         tape->pos[i + 1].delay = 1;
8819
8820         // delay part
8821         for (j = 0; j < MAX_PLAYERS; j++)
8822           tape->pos[i].action[j] = MV_NONE;
8823         tape->pos[i].delay--;
8824
8825         i++;
8826         tape->length++;
8827       }
8828     }
8829
8830     if (checkEndOfFile(file))
8831       break;
8832   }
8833
8834   if (i != tape->length)
8835     chunk_size = tape_pos_size * i;
8836
8837   return chunk_size;
8838 }
8839
8840 static void LoadTape_SokobanSolution(char *filename)
8841 {
8842   File *file;
8843   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8844
8845   if (!(file = openFile(filename, MODE_READ)))
8846   {
8847     tape.no_valid_file = TRUE;
8848
8849     return;
8850   }
8851
8852   while (!checkEndOfFile(file))
8853   {
8854     unsigned char c = getByteFromFile(file);
8855
8856     if (checkEndOfFile(file))
8857       break;
8858
8859     switch (c)
8860     {
8861       case 'u':
8862       case 'U':
8863         tape.pos[tape.length].action[0] = MV_UP;
8864         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8865         tape.length++;
8866         break;
8867
8868       case 'd':
8869       case 'D':
8870         tape.pos[tape.length].action[0] = MV_DOWN;
8871         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8872         tape.length++;
8873         break;
8874
8875       case 'l':
8876       case 'L':
8877         tape.pos[tape.length].action[0] = MV_LEFT;
8878         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8879         tape.length++;
8880         break;
8881
8882       case 'r':
8883       case 'R':
8884         tape.pos[tape.length].action[0] = MV_RIGHT;
8885         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8886         tape.length++;
8887         break;
8888
8889       case '\n':
8890       case '\r':
8891       case '\t':
8892       case ' ':
8893         // ignore white-space characters
8894         break;
8895
8896       default:
8897         tape.no_valid_file = TRUE;
8898
8899         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8900
8901         break;
8902     }
8903   }
8904
8905   closeFile(file);
8906
8907   if (tape.no_valid_file)
8908     return;
8909
8910   tape.length_frames  = GetTapeLengthFrames();
8911   tape.length_seconds = GetTapeLengthSeconds();
8912 }
8913
8914 void LoadTapeFromFilename(char *filename)
8915 {
8916   char cookie[MAX_LINE_LEN];
8917   char chunk_name[CHUNK_ID_LEN + 1];
8918   File *file;
8919   int chunk_size;
8920
8921   // always start with reliable default values
8922   setTapeInfoToDefaults();
8923
8924   if (strSuffix(filename, ".sln"))
8925   {
8926     LoadTape_SokobanSolution(filename);
8927
8928     return;
8929   }
8930
8931   if (!(file = openFile(filename, MODE_READ)))
8932   {
8933     tape.no_valid_file = TRUE;
8934
8935     return;
8936   }
8937
8938   getFileChunkBE(file, chunk_name, NULL);
8939   if (strEqual(chunk_name, "RND1"))
8940   {
8941     getFile32BitBE(file);               // not used
8942
8943     getFileChunkBE(file, chunk_name, NULL);
8944     if (!strEqual(chunk_name, "TAPE"))
8945     {
8946       tape.no_valid_file = TRUE;
8947
8948       Warn("unknown format of tape file '%s'", filename);
8949
8950       closeFile(file);
8951
8952       return;
8953     }
8954   }
8955   else  // check for pre-2.0 file format with cookie string
8956   {
8957     strcpy(cookie, chunk_name);
8958     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8959       cookie[4] = '\0';
8960     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8961       cookie[strlen(cookie) - 1] = '\0';
8962
8963     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8964     {
8965       tape.no_valid_file = TRUE;
8966
8967       Warn("unknown format of tape file '%s'", filename);
8968
8969       closeFile(file);
8970
8971       return;
8972     }
8973
8974     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8975     {
8976       tape.no_valid_file = TRUE;
8977
8978       Warn("unsupported version of tape file '%s'", filename);
8979
8980       closeFile(file);
8981
8982       return;
8983     }
8984
8985     // pre-2.0 tape files have no game version, so use file version here
8986     tape.game_version = tape.file_version;
8987   }
8988
8989   if (tape.file_version < FILE_VERSION_1_2)
8990   {
8991     // tape files from versions before 1.2.0 without chunk structure
8992     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8993     LoadTape_BODY(file, 2 * tape.length,      &tape);
8994   }
8995   else
8996   {
8997     static struct
8998     {
8999       char *name;
9000       int size;
9001       int (*loader)(File *, int, struct TapeInfo *);
9002     }
9003     chunk_info[] =
9004     {
9005       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9006       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9007       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9008       { "INFO", -1,                     LoadTape_INFO },
9009       { "BODY", -1,                     LoadTape_BODY },
9010       {  NULL,  0,                      NULL }
9011     };
9012
9013     while (getFileChunkBE(file, chunk_name, &chunk_size))
9014     {
9015       int i = 0;
9016
9017       while (chunk_info[i].name != NULL &&
9018              !strEqual(chunk_name, chunk_info[i].name))
9019         i++;
9020
9021       if (chunk_info[i].name == NULL)
9022       {
9023         Warn("unknown chunk '%s' in tape file '%s'",
9024               chunk_name, filename);
9025
9026         ReadUnusedBytesFromFile(file, chunk_size);
9027       }
9028       else if (chunk_info[i].size != -1 &&
9029                chunk_info[i].size != chunk_size)
9030       {
9031         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9032               chunk_size, chunk_name, filename);
9033
9034         ReadUnusedBytesFromFile(file, chunk_size);
9035       }
9036       else
9037       {
9038         // call function to load this tape chunk
9039         int chunk_size_expected =
9040           (chunk_info[i].loader)(file, chunk_size, &tape);
9041
9042         // the size of some chunks cannot be checked before reading other
9043         // chunks first (like "HEAD" and "BODY") that contain some header
9044         // information, so check them here
9045         if (chunk_size_expected != chunk_size)
9046         {
9047           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9048                 chunk_size, chunk_name, filename);
9049         }
9050       }
9051     }
9052   }
9053
9054   closeFile(file);
9055
9056   tape.length_frames  = GetTapeLengthFrames();
9057   tape.length_seconds = GetTapeLengthSeconds();
9058
9059 #if 0
9060   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9061         tape.file_version);
9062   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9063         tape.game_version);
9064   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9065         tape.engine_version);
9066 #endif
9067 }
9068
9069 void LoadTape(int nr)
9070 {
9071   char *filename = getTapeFilename(nr);
9072
9073   LoadTapeFromFilename(filename);
9074 }
9075
9076 void LoadSolutionTape(int nr)
9077 {
9078   char *filename = getSolutionTapeFilename(nr);
9079
9080   LoadTapeFromFilename(filename);
9081
9082   if (TAPE_IS_EMPTY(tape))
9083   {
9084     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9085         level.native_bd_level->replay != NULL)
9086       CopyNativeTape_BD_to_RND(&level);
9087     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9088         level.native_sp_level->demo.is_available)
9089       CopyNativeTape_SP_to_RND(&level);
9090   }
9091 }
9092
9093 void LoadScoreTape(char *score_tape_basename, int nr)
9094 {
9095   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9096
9097   LoadTapeFromFilename(filename);
9098 }
9099
9100 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9101 {
9102   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9103
9104   LoadTapeFromFilename(filename);
9105 }
9106
9107 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9108 {
9109   // chunk required for team mode tapes with non-default screen size
9110   return (tape->num_participating_players > 1 &&
9111           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9112            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9113 }
9114
9115 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9116 {
9117   putFileVersion(file, tape->file_version);
9118   putFileVersion(file, tape->game_version);
9119 }
9120
9121 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9122 {
9123   int i;
9124   byte store_participating_players = 0;
9125
9126   // set bits for participating players for compact storage
9127   for (i = 0; i < MAX_PLAYERS; i++)
9128     if (tape->player_participates[i])
9129       store_participating_players |= (1 << i);
9130
9131   putFile32BitBE(file, tape->random_seed);
9132   putFile32BitBE(file, tape->date);
9133   putFile32BitBE(file, tape->length);
9134
9135   putFile8Bit(file, store_participating_players);
9136
9137   putFile8Bit(file, getTapeActionValue(tape));
9138
9139   putFile8Bit(file, tape->property_bits);
9140   putFile8Bit(file, tape->solved);
9141
9142   putFileVersion(file, tape->engine_version);
9143 }
9144
9145 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9146 {
9147   putFile8Bit(file, tape->scr_fieldx);
9148   putFile8Bit(file, tape->scr_fieldy);
9149 }
9150
9151 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9152 {
9153   int level_identifier_size = strlen(tape->level_identifier) + 1;
9154   int i;
9155
9156   putFile16BitBE(file, level_identifier_size);
9157
9158   for (i = 0; i < level_identifier_size; i++)
9159     putFile8Bit(file, tape->level_identifier[i]);
9160
9161   putFile16BitBE(file, tape->level_nr);
9162 }
9163
9164 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9165 {
9166   int i, j;
9167
9168   for (i = 0; i < tape->length; i++)
9169   {
9170     if (tape->use_key_actions)
9171     {
9172       for (j = 0; j < MAX_PLAYERS; j++)
9173         if (tape->player_participates[j])
9174           putFile8Bit(file, tape->pos[i].action[j]);
9175     }
9176
9177     if (tape->use_mouse_actions)
9178     {
9179       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9180       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9181       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9182     }
9183
9184     putFile8Bit(file, tape->pos[i].delay);
9185   }
9186 }
9187
9188 void SaveTapeToFilename(char *filename)
9189 {
9190   FILE *file;
9191   int tape_pos_size;
9192   int info_chunk_size;
9193   int body_chunk_size;
9194
9195   if (!(file = fopen(filename, MODE_WRITE)))
9196   {
9197     Warn("cannot save level recording file '%s'", filename);
9198
9199     return;
9200   }
9201
9202   tape_pos_size = getTapePosSize(&tape);
9203
9204   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9205   body_chunk_size = tape_pos_size * tape.length;
9206
9207   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9208   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9209
9210   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9211   SaveTape_VERS(file, &tape);
9212
9213   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9214   SaveTape_HEAD(file, &tape);
9215
9216   if (checkSaveTape_SCRN(&tape))
9217   {
9218     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9219     SaveTape_SCRN(file, &tape);
9220   }
9221
9222   putFileChunkBE(file, "INFO", info_chunk_size);
9223   SaveTape_INFO(file, &tape);
9224
9225   putFileChunkBE(file, "BODY", body_chunk_size);
9226   SaveTape_BODY(file, &tape);
9227
9228   fclose(file);
9229
9230   SetFilePermissions(filename, PERMS_PRIVATE);
9231 }
9232
9233 static void SaveTapeExt(char *filename)
9234 {
9235   int i;
9236
9237   tape.file_version = FILE_VERSION_ACTUAL;
9238   tape.game_version = GAME_VERSION_ACTUAL;
9239
9240   tape.num_participating_players = 0;
9241
9242   // count number of participating players
9243   for (i = 0; i < MAX_PLAYERS; i++)
9244     if (tape.player_participates[i])
9245       tape.num_participating_players++;
9246
9247   SaveTapeToFilename(filename);
9248
9249   tape.changed = FALSE;
9250 }
9251
9252 void SaveTape(int nr)
9253 {
9254   char *filename = getTapeFilename(nr);
9255
9256   InitTapeDirectory(leveldir_current->subdir);
9257
9258   SaveTapeExt(filename);
9259 }
9260
9261 void SaveScoreTape(int nr)
9262 {
9263   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9264
9265   // used instead of "leveldir_current->subdir" (for network games)
9266   InitScoreTapeDirectory(levelset.identifier, nr);
9267
9268   SaveTapeExt(filename);
9269 }
9270
9271 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9272                                   unsigned int req_state_added)
9273 {
9274   char *filename = getTapeFilename(nr);
9275   boolean new_tape = !fileExists(filename);
9276   boolean tape_saved = FALSE;
9277
9278   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9279   {
9280     SaveTape(nr);
9281
9282     if (new_tape)
9283       Request(msg_saved, REQ_CONFIRM | req_state_added);
9284
9285     tape_saved = TRUE;
9286   }
9287
9288   return tape_saved;
9289 }
9290
9291 boolean SaveTapeChecked(int nr)
9292 {
9293   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9294 }
9295
9296 boolean SaveTapeChecked_LevelSolved(int nr)
9297 {
9298   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9299                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9300 }
9301
9302 void DumpTape(struct TapeInfo *tape)
9303 {
9304   int tape_frame_counter;
9305   int i, j;
9306
9307   if (tape->no_valid_file)
9308   {
9309     Warn("cannot dump -- no valid tape file found");
9310
9311     return;
9312   }
9313
9314   PrintLine("-", 79);
9315
9316   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9317         tape->level_nr, tape->file_version, tape->game_version);
9318   Print("                  (effective engine version %08d)\n",
9319         tape->engine_version);
9320   Print("Level series identifier: '%s'\n", tape->level_identifier);
9321
9322   Print("Solution tape: %s\n",
9323         tape->solved ? "yes" :
9324         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9325
9326   Print("Special tape properties: ");
9327   if (tape->property_bits == TAPE_PROPERTY_NONE)
9328     Print("[none]");
9329   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9330     Print("[em_random_bug]");
9331   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9332     Print("[game_speed]");
9333   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9334     Print("[pause]");
9335   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9336     Print("[single_step]");
9337   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9338     Print("[snapshot]");
9339   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9340     Print("[replayed]");
9341   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9342     Print("[tas_keys]");
9343   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9344     Print("[small_graphics]");
9345   Print("\n");
9346
9347   int year2 = tape->date / 10000;
9348   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9349   int month_index_raw = (tape->date / 100) % 100;
9350   int month_index = month_index_raw % 12;       // prevent invalid index
9351   int month = month_index + 1;
9352   int day = tape->date % 100;
9353
9354   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9355
9356   PrintLine("-", 79);
9357
9358   tape_frame_counter = 0;
9359
9360   for (i = 0; i < tape->length; i++)
9361   {
9362     if (i >= MAX_TAPE_LEN)
9363       break;
9364
9365     Print("%04d: ", i);
9366
9367     for (j = 0; j < MAX_PLAYERS; j++)
9368     {
9369       if (tape->player_participates[j])
9370       {
9371         int action = tape->pos[i].action[j];
9372
9373         Print("%d:%02x ", j, action);
9374         Print("[%c%c%c%c|%c%c] - ",
9375               (action & JOY_LEFT ? '<' : ' '),
9376               (action & JOY_RIGHT ? '>' : ' '),
9377               (action & JOY_UP ? '^' : ' '),
9378               (action & JOY_DOWN ? 'v' : ' '),
9379               (action & JOY_BUTTON_1 ? '1' : ' '),
9380               (action & JOY_BUTTON_2 ? '2' : ' '));
9381       }
9382     }
9383
9384     Print("(%03d) ", tape->pos[i].delay);
9385     Print("[%05d]\n", tape_frame_counter);
9386
9387     tape_frame_counter += tape->pos[i].delay;
9388   }
9389
9390   PrintLine("-", 79);
9391 }
9392
9393 void DumpTapes(void)
9394 {
9395   static LevelDirTree *dumptape_leveldir = NULL;
9396
9397   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9398                                                 global.dumptape_leveldir);
9399
9400   if (dumptape_leveldir == NULL)
9401     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9402
9403   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9404       global.dumptape_level_nr > dumptape_leveldir->last_level)
9405     Fail("no such level number: %d", global.dumptape_level_nr);
9406
9407   leveldir_current = dumptape_leveldir;
9408
9409   if (options.mytapes)
9410     LoadTape(global.dumptape_level_nr);
9411   else
9412     LoadSolutionTape(global.dumptape_level_nr);
9413
9414   DumpTape(&tape);
9415
9416   CloseAllAndExit(0);
9417 }
9418
9419
9420 // ============================================================================
9421 // score file functions
9422 // ============================================================================
9423
9424 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9425 {
9426   int i;
9427
9428   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9429   {
9430     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9431     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9432     scores->entry[i].score = 0;
9433     scores->entry[i].time = 0;
9434
9435     scores->entry[i].id = -1;
9436     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9437     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9438     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9439     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9440     strcpy(scores->entry[i].country_code, "??");
9441   }
9442
9443   scores->num_entries = 0;
9444   scores->last_added = -1;
9445   scores->last_added_local = -1;
9446
9447   scores->updated = FALSE;
9448   scores->uploaded = FALSE;
9449   scores->tape_downloaded = FALSE;
9450   scores->force_last_added = FALSE;
9451
9452   // The following values are intentionally not reset here:
9453   // - last_level_nr
9454   // - last_entry_nr
9455   // - next_level_nr
9456   // - continue_playing
9457   // - continue_on_return
9458 }
9459
9460 static void setScoreInfoToDefaults(void)
9461 {
9462   setScoreInfoToDefaultsExt(&scores);
9463 }
9464
9465 static void setServerScoreInfoToDefaults(void)
9466 {
9467   setScoreInfoToDefaultsExt(&server_scores);
9468 }
9469
9470 static void LoadScore_OLD(int nr)
9471 {
9472   int i;
9473   char *filename = getScoreFilename(nr);
9474   char cookie[MAX_LINE_LEN];
9475   char line[MAX_LINE_LEN];
9476   char *line_ptr;
9477   FILE *file;
9478
9479   if (!(file = fopen(filename, MODE_READ)))
9480     return;
9481
9482   // check file identifier
9483   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9484     cookie[0] = '\0';
9485   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9486     cookie[strlen(cookie) - 1] = '\0';
9487
9488   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9489   {
9490     Warn("unknown format of score file '%s'", filename);
9491
9492     fclose(file);
9493
9494     return;
9495   }
9496
9497   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9498   {
9499     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9500       Warn("fscanf() failed; %s", strerror(errno));
9501
9502     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9503       line[0] = '\0';
9504
9505     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9506       line[strlen(line) - 1] = '\0';
9507
9508     for (line_ptr = line; *line_ptr; line_ptr++)
9509     {
9510       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9511       {
9512         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9513         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9514         break;
9515       }
9516     }
9517   }
9518
9519   fclose(file);
9520 }
9521
9522 static void ConvertScore_OLD(void)
9523 {
9524   // only convert score to time for levels that rate playing time over score
9525   if (!level.rate_time_over_score)
9526     return;
9527
9528   // convert old score to playing time for score-less levels (like Supaplex)
9529   int time_final_max = 999;
9530   int i;
9531
9532   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9533   {
9534     int score = scores.entry[i].score;
9535
9536     if (score > 0 && score < time_final_max)
9537       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9538   }
9539 }
9540
9541 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9542 {
9543   scores->file_version = getFileVersion(file);
9544   scores->game_version = getFileVersion(file);
9545
9546   return chunk_size;
9547 }
9548
9549 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9550 {
9551   char *level_identifier = NULL;
9552   int level_identifier_size;
9553   int i;
9554
9555   level_identifier_size = getFile16BitBE(file);
9556
9557   level_identifier = checked_malloc(level_identifier_size);
9558
9559   for (i = 0; i < level_identifier_size; i++)
9560     level_identifier[i] = getFile8Bit(file);
9561
9562   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9563   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9564
9565   checked_free(level_identifier);
9566
9567   scores->level_nr = getFile16BitBE(file);
9568   scores->num_entries = getFile16BitBE(file);
9569
9570   chunk_size = 2 + level_identifier_size + 2 + 2;
9571
9572   return chunk_size;
9573 }
9574
9575 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9576 {
9577   int i, j;
9578
9579   for (i = 0; i < scores->num_entries; i++)
9580   {
9581     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9582       scores->entry[i].name[j] = getFile8Bit(file);
9583
9584     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9585   }
9586
9587   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9588
9589   return chunk_size;
9590 }
9591
9592 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9593 {
9594   int i;
9595
9596   for (i = 0; i < scores->num_entries; i++)
9597     scores->entry[i].score = getFile16BitBE(file);
9598
9599   chunk_size = scores->num_entries * 2;
9600
9601   return chunk_size;
9602 }
9603
9604 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9605 {
9606   int i;
9607
9608   for (i = 0; i < scores->num_entries; i++)
9609     scores->entry[i].score = getFile32BitBE(file);
9610
9611   chunk_size = scores->num_entries * 4;
9612
9613   return chunk_size;
9614 }
9615
9616 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9617 {
9618   int i;
9619
9620   for (i = 0; i < scores->num_entries; i++)
9621     scores->entry[i].time = getFile32BitBE(file);
9622
9623   chunk_size = scores->num_entries * 4;
9624
9625   return chunk_size;
9626 }
9627
9628 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9629 {
9630   int i, j;
9631
9632   for (i = 0; i < scores->num_entries; i++)
9633   {
9634     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9635       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9636
9637     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9638   }
9639
9640   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9641
9642   return chunk_size;
9643 }
9644
9645 void LoadScore(int nr)
9646 {
9647   char *filename = getScoreFilename(nr);
9648   char cookie[MAX_LINE_LEN];
9649   char chunk_name[CHUNK_ID_LEN + 1];
9650   int chunk_size;
9651   boolean old_score_file_format = FALSE;
9652   File *file;
9653
9654   // always start with reliable default values
9655   setScoreInfoToDefaults();
9656
9657   if (!(file = openFile(filename, MODE_READ)))
9658     return;
9659
9660   getFileChunkBE(file, chunk_name, NULL);
9661   if (strEqual(chunk_name, "RND1"))
9662   {
9663     getFile32BitBE(file);               // not used
9664
9665     getFileChunkBE(file, chunk_name, NULL);
9666     if (!strEqual(chunk_name, "SCOR"))
9667     {
9668       Warn("unknown format of score file '%s'", filename);
9669
9670       closeFile(file);
9671
9672       return;
9673     }
9674   }
9675   else  // check for old file format with cookie string
9676   {
9677     strcpy(cookie, chunk_name);
9678     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9679       cookie[4] = '\0';
9680     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9681       cookie[strlen(cookie) - 1] = '\0';
9682
9683     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9684     {
9685       Warn("unknown format of score file '%s'", filename);
9686
9687       closeFile(file);
9688
9689       return;
9690     }
9691
9692     old_score_file_format = TRUE;
9693   }
9694
9695   if (old_score_file_format)
9696   {
9697     // score files from versions before 4.2.4.0 without chunk structure
9698     LoadScore_OLD(nr);
9699
9700     // convert score to time, if possible (mainly for Supaplex levels)
9701     ConvertScore_OLD();
9702   }
9703   else
9704   {
9705     static struct
9706     {
9707       char *name;
9708       int size;
9709       int (*loader)(File *, int, struct ScoreInfo *);
9710     }
9711     chunk_info[] =
9712     {
9713       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9714       { "INFO", -1,                     LoadScore_INFO },
9715       { "NAME", -1,                     LoadScore_NAME },
9716       { "SCOR", -1,                     LoadScore_SCOR },
9717       { "SC4R", -1,                     LoadScore_SC4R },
9718       { "TIME", -1,                     LoadScore_TIME },
9719       { "TAPE", -1,                     LoadScore_TAPE },
9720
9721       {  NULL,  0,                      NULL }
9722     };
9723
9724     while (getFileChunkBE(file, chunk_name, &chunk_size))
9725     {
9726       int i = 0;
9727
9728       while (chunk_info[i].name != NULL &&
9729              !strEqual(chunk_name, chunk_info[i].name))
9730         i++;
9731
9732       if (chunk_info[i].name == NULL)
9733       {
9734         Warn("unknown chunk '%s' in score file '%s'",
9735               chunk_name, filename);
9736
9737         ReadUnusedBytesFromFile(file, chunk_size);
9738       }
9739       else if (chunk_info[i].size != -1 &&
9740                chunk_info[i].size != chunk_size)
9741       {
9742         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9743               chunk_size, chunk_name, filename);
9744
9745         ReadUnusedBytesFromFile(file, chunk_size);
9746       }
9747       else
9748       {
9749         // call function to load this score chunk
9750         int chunk_size_expected =
9751           (chunk_info[i].loader)(file, chunk_size, &scores);
9752
9753         // the size of some chunks cannot be checked before reading other
9754         // chunks first (like "HEAD" and "BODY") that contain some header
9755         // information, so check them here
9756         if (chunk_size_expected != chunk_size)
9757         {
9758           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9759                 chunk_size, chunk_name, filename);
9760         }
9761       }
9762     }
9763   }
9764
9765   closeFile(file);
9766 }
9767
9768 #if ENABLE_HISTORIC_CHUNKS
9769 void SaveScore_OLD(int nr)
9770 {
9771   int i;
9772   char *filename = getScoreFilename(nr);
9773   FILE *file;
9774
9775   // used instead of "leveldir_current->subdir" (for network games)
9776   InitScoreDirectory(levelset.identifier);
9777
9778   if (!(file = fopen(filename, MODE_WRITE)))
9779   {
9780     Warn("cannot save score for level %d", nr);
9781
9782     return;
9783   }
9784
9785   fprintf(file, "%s\n\n", SCORE_COOKIE);
9786
9787   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9788     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9789
9790   fclose(file);
9791
9792   SetFilePermissions(filename, PERMS_PRIVATE);
9793 }
9794 #endif
9795
9796 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9797 {
9798   putFileVersion(file, scores->file_version);
9799   putFileVersion(file, scores->game_version);
9800 }
9801
9802 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9803 {
9804   int level_identifier_size = strlen(scores->level_identifier) + 1;
9805   int i;
9806
9807   putFile16BitBE(file, level_identifier_size);
9808
9809   for (i = 0; i < level_identifier_size; i++)
9810     putFile8Bit(file, scores->level_identifier[i]);
9811
9812   putFile16BitBE(file, scores->level_nr);
9813   putFile16BitBE(file, scores->num_entries);
9814 }
9815
9816 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9817 {
9818   int i, j;
9819
9820   for (i = 0; i < scores->num_entries; i++)
9821   {
9822     int name_size = strlen(scores->entry[i].name);
9823
9824     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9825       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9826   }
9827 }
9828
9829 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9830 {
9831   int i;
9832
9833   for (i = 0; i < scores->num_entries; i++)
9834     putFile16BitBE(file, scores->entry[i].score);
9835 }
9836
9837 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9838 {
9839   int i;
9840
9841   for (i = 0; i < scores->num_entries; i++)
9842     putFile32BitBE(file, scores->entry[i].score);
9843 }
9844
9845 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9846 {
9847   int i;
9848
9849   for (i = 0; i < scores->num_entries; i++)
9850     putFile32BitBE(file, scores->entry[i].time);
9851 }
9852
9853 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9854 {
9855   int i, j;
9856
9857   for (i = 0; i < scores->num_entries; i++)
9858   {
9859     int size = strlen(scores->entry[i].tape_basename);
9860
9861     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9862       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9863   }
9864 }
9865
9866 static void SaveScoreToFilename(char *filename)
9867 {
9868   FILE *file;
9869   int info_chunk_size;
9870   int name_chunk_size;
9871   int scor_chunk_size;
9872   int sc4r_chunk_size;
9873   int time_chunk_size;
9874   int tape_chunk_size;
9875   boolean has_large_score_values;
9876   int i;
9877
9878   if (!(file = fopen(filename, MODE_WRITE)))
9879   {
9880     Warn("cannot save score file '%s'", filename);
9881
9882     return;
9883   }
9884
9885   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9886   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9887   scor_chunk_size = scores.num_entries * 2;
9888   sc4r_chunk_size = scores.num_entries * 4;
9889   time_chunk_size = scores.num_entries * 4;
9890   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9891
9892   has_large_score_values = FALSE;
9893   for (i = 0; i < scores.num_entries; i++)
9894     if (scores.entry[i].score > 0xffff)
9895       has_large_score_values = TRUE;
9896
9897   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9898   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9899
9900   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9901   SaveScore_VERS(file, &scores);
9902
9903   putFileChunkBE(file, "INFO", info_chunk_size);
9904   SaveScore_INFO(file, &scores);
9905
9906   putFileChunkBE(file, "NAME", name_chunk_size);
9907   SaveScore_NAME(file, &scores);
9908
9909   if (has_large_score_values)
9910   {
9911     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9912     SaveScore_SC4R(file, &scores);
9913   }
9914   else
9915   {
9916     putFileChunkBE(file, "SCOR", scor_chunk_size);
9917     SaveScore_SCOR(file, &scores);
9918   }
9919
9920   putFileChunkBE(file, "TIME", time_chunk_size);
9921   SaveScore_TIME(file, &scores);
9922
9923   putFileChunkBE(file, "TAPE", tape_chunk_size);
9924   SaveScore_TAPE(file, &scores);
9925
9926   fclose(file);
9927
9928   SetFilePermissions(filename, PERMS_PRIVATE);
9929 }
9930
9931 void SaveScore(int nr)
9932 {
9933   char *filename = getScoreFilename(nr);
9934   int i;
9935
9936   // used instead of "leveldir_current->subdir" (for network games)
9937   InitScoreDirectory(levelset.identifier);
9938
9939   scores.file_version = FILE_VERSION_ACTUAL;
9940   scores.game_version = GAME_VERSION_ACTUAL;
9941
9942   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9943   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9944   scores.level_nr = level_nr;
9945
9946   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9947     if (scores.entry[i].score == 0 &&
9948         scores.entry[i].time == 0 &&
9949         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9950       break;
9951
9952   scores.num_entries = i;
9953
9954   if (scores.num_entries == 0)
9955     return;
9956
9957   SaveScoreToFilename(filename);
9958 }
9959
9960 static void LoadServerScoreFromCache(int nr)
9961 {
9962   struct ScoreEntry score_entry;
9963   struct
9964   {
9965     void *value;
9966     boolean is_string;
9967     int string_size;
9968   }
9969   score_mapping[] =
9970   {
9971     { &score_entry.score,               FALSE,  0                       },
9972     { &score_entry.time,                FALSE,  0                       },
9973     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
9974     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
9975     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
9976     { &score_entry.id,                  FALSE,  0                       },
9977     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
9978     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
9979     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
9980     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
9981
9982     { NULL,                             FALSE,  0                       }
9983   };
9984   char *filename = getScoreCacheFilename(nr);
9985   SetupFileHash *score_hash = loadSetupFileHash(filename);
9986   int i, j;
9987
9988   server_scores.num_entries = 0;
9989
9990   if (score_hash == NULL)
9991     return;
9992
9993   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9994   {
9995     score_entry = server_scores.entry[i];
9996
9997     for (j = 0; score_mapping[j].value != NULL; j++)
9998     {
9999       char token[10];
10000
10001       sprintf(token, "%02d.%d", i, j);
10002
10003       char *value = getHashEntry(score_hash, token);
10004
10005       if (value == NULL)
10006         continue;
10007
10008       if (score_mapping[j].is_string)
10009       {
10010         char *score_value = (char *)score_mapping[j].value;
10011         int value_size = score_mapping[j].string_size;
10012
10013         strncpy(score_value, value, value_size);
10014         score_value[value_size] = '\0';
10015       }
10016       else
10017       {
10018         int *score_value = (int *)score_mapping[j].value;
10019
10020         *score_value = atoi(value);
10021       }
10022
10023       server_scores.num_entries = i + 1;
10024     }
10025
10026     server_scores.entry[i] = score_entry;
10027   }
10028
10029   freeSetupFileHash(score_hash);
10030 }
10031
10032 void LoadServerScore(int nr, boolean download_score)
10033 {
10034   if (!setup.use_api_server)
10035     return;
10036
10037   // always start with reliable default values
10038   setServerScoreInfoToDefaults();
10039
10040   // 1st step: load server scores from cache file (which may not exist)
10041   // (this should prevent reading it while the thread is writing to it)
10042   LoadServerScoreFromCache(nr);
10043
10044   if (download_score && runtime.use_api_server)
10045   {
10046     // 2nd step: download server scores from score server to cache file
10047     // (as thread, as it might time out if the server is not reachable)
10048     ApiGetScoreAsThread(nr);
10049   }
10050 }
10051
10052 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10053 {
10054   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10055
10056   // if score tape not uploaded, ask for uploading missing tapes later
10057   if (!setup.has_remaining_tapes)
10058     setup.ask_for_remaining_tapes = TRUE;
10059
10060   setup.provide_uploading_tapes = TRUE;
10061   setup.has_remaining_tapes = TRUE;
10062
10063   SaveSetup_ServerSetup();
10064 }
10065
10066 void SaveServerScore(int nr, boolean tape_saved)
10067 {
10068   if (!runtime.use_api_server)
10069   {
10070     PrepareScoreTapesForUpload(leveldir_current->subdir);
10071
10072     return;
10073   }
10074
10075   ApiAddScoreAsThread(nr, tape_saved, NULL);
10076 }
10077
10078 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10079                              char *score_tape_filename)
10080 {
10081   if (!runtime.use_api_server)
10082     return;
10083
10084   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10085 }
10086
10087 void LoadLocalAndServerScore(int nr, boolean download_score)
10088 {
10089   int last_added_local = scores.last_added_local;
10090   boolean force_last_added = scores.force_last_added;
10091
10092   // needed if only showing server scores
10093   setScoreInfoToDefaults();
10094
10095   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10096     LoadScore(nr);
10097
10098   // restore last added local score entry (before merging server scores)
10099   scores.last_added = scores.last_added_local = last_added_local;
10100
10101   if (setup.use_api_server &&
10102       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10103   {
10104     // load server scores from cache file and trigger update from server
10105     LoadServerScore(nr, download_score);
10106
10107     // merge local scores with scores from server
10108     MergeServerScore();
10109   }
10110
10111   if (force_last_added)
10112     scores.force_last_added = force_last_added;
10113 }
10114
10115
10116 // ============================================================================
10117 // setup file functions
10118 // ============================================================================
10119
10120 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10121
10122
10123 static struct TokenInfo global_setup_tokens[] =
10124 {
10125   {
10126     TYPE_STRING,
10127     &setup.player_name,                         "player_name"
10128   },
10129   {
10130     TYPE_SWITCH,
10131     &setup.multiple_users,                      "multiple_users"
10132   },
10133   {
10134     TYPE_SWITCH,
10135     &setup.sound,                               "sound"
10136   },
10137   {
10138     TYPE_SWITCH,
10139     &setup.sound_loops,                         "repeating_sound_loops"
10140   },
10141   {
10142     TYPE_SWITCH,
10143     &setup.sound_music,                         "background_music"
10144   },
10145   {
10146     TYPE_SWITCH,
10147     &setup.sound_simple,                        "simple_sound_effects"
10148   },
10149   {
10150     TYPE_SWITCH,
10151     &setup.toons,                               "toons"
10152   },
10153   {
10154     TYPE_SWITCH,
10155     &setup.global_animations,                   "global_animations"
10156   },
10157   {
10158     TYPE_SWITCH,
10159     &setup.scroll_delay,                        "scroll_delay"
10160   },
10161   {
10162     TYPE_SWITCH,
10163     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10164   },
10165   {
10166     TYPE_INTEGER,
10167     &setup.scroll_delay_value,                  "scroll_delay_value"
10168   },
10169   {
10170     TYPE_STRING,
10171     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10172   },
10173   {
10174     TYPE_INTEGER,
10175     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10176   },
10177   {
10178     TYPE_SWITCH,
10179     &setup.fade_screens,                        "fade_screens"
10180   },
10181   {
10182     TYPE_SWITCH,
10183     &setup.autorecord,                          "automatic_tape_recording"
10184   },
10185   {
10186     TYPE_SWITCH,
10187     &setup.autorecord_after_replay,             "autorecord_after_replay"
10188   },
10189   {
10190     TYPE_SWITCH,
10191     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10192   },
10193   {
10194     TYPE_SWITCH,
10195     &setup.show_titlescreen,                    "show_titlescreen"
10196   },
10197   {
10198     TYPE_SWITCH,
10199     &setup.quick_doors,                         "quick_doors"
10200   },
10201   {
10202     TYPE_SWITCH,
10203     &setup.team_mode,                           "team_mode"
10204   },
10205   {
10206     TYPE_SWITCH,
10207     &setup.handicap,                            "handicap"
10208   },
10209   {
10210     TYPE_SWITCH,
10211     &setup.skip_levels,                         "skip_levels"
10212   },
10213   {
10214     TYPE_SWITCH,
10215     &setup.increment_levels,                    "increment_levels"
10216   },
10217   {
10218     TYPE_SWITCH,
10219     &setup.auto_play_next_level,                "auto_play_next_level"
10220   },
10221   {
10222     TYPE_SWITCH,
10223     &setup.count_score_after_game,              "count_score_after_game"
10224   },
10225   {
10226     TYPE_SWITCH,
10227     &setup.show_scores_after_game,              "show_scores_after_game"
10228   },
10229   {
10230     TYPE_SWITCH,
10231     &setup.time_limit,                          "time_limit"
10232   },
10233   {
10234     TYPE_SWITCH,
10235     &setup.fullscreen,                          "fullscreen"
10236   },
10237   {
10238     TYPE_INTEGER,
10239     &setup.window_scaling_percent,              "window_scaling_percent"
10240   },
10241   {
10242     TYPE_STRING,
10243     &setup.window_scaling_quality,              "window_scaling_quality"
10244   },
10245   {
10246     TYPE_STRING,
10247     &setup.screen_rendering_mode,               "screen_rendering_mode"
10248   },
10249   {
10250     TYPE_STRING,
10251     &setup.vsync_mode,                          "vsync_mode"
10252   },
10253   {
10254     TYPE_SWITCH,
10255     &setup.ask_on_escape,                       "ask_on_escape"
10256   },
10257   {
10258     TYPE_SWITCH,
10259     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10260   },
10261   {
10262     TYPE_SWITCH,
10263     &setup.ask_on_game_over,                    "ask_on_game_over"
10264   },
10265   {
10266     TYPE_SWITCH,
10267     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10268   },
10269   {
10270     TYPE_SWITCH,
10271     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10272   },
10273   {
10274     TYPE_SWITCH,
10275     &setup.quick_switch,                        "quick_player_switch"
10276   },
10277   {
10278     TYPE_SWITCH,
10279     &setup.input_on_focus,                      "input_on_focus"
10280   },
10281   {
10282     TYPE_SWITCH,
10283     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10284   },
10285   {
10286     TYPE_SWITCH,
10287     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10288   },
10289   {
10290     TYPE_SWITCH,
10291     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10292   },
10293   {
10294     TYPE_SWITCH,
10295     &setup.game_speed_extended,                 "game_speed_extended"
10296   },
10297   {
10298     TYPE_INTEGER,
10299     &setup.game_frame_delay,                    "game_frame_delay"
10300   },
10301   {
10302     TYPE_SWITCH,
10303     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10304   },
10305   {
10306     TYPE_SWITCH,
10307     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10308   },
10309   {
10310     TYPE_SWITCH,
10311     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10312   },
10313   {
10314     TYPE_SWITCH3,
10315     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10316   },
10317   {
10318     TYPE_SWITCH,
10319     &setup.sp_show_border_elements,             "sp_show_border_elements"
10320   },
10321   {
10322     TYPE_SWITCH,
10323     &setup.small_game_graphics,                 "small_game_graphics"
10324   },
10325   {
10326     TYPE_SWITCH,
10327     &setup.show_load_save_buttons,              "show_load_save_buttons"
10328   },
10329   {
10330     TYPE_SWITCH,
10331     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10332   },
10333   {
10334     TYPE_STRING,
10335     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10336   },
10337   {
10338     TYPE_STRING,
10339     &setup.graphics_set,                        "graphics_set"
10340   },
10341   {
10342     TYPE_STRING,
10343     &setup.sounds_set,                          "sounds_set"
10344   },
10345   {
10346     TYPE_STRING,
10347     &setup.music_set,                           "music_set"
10348   },
10349   {
10350     TYPE_SWITCH3,
10351     &setup.override_level_graphics,             "override_level_graphics"
10352   },
10353   {
10354     TYPE_SWITCH3,
10355     &setup.override_level_sounds,               "override_level_sounds"
10356   },
10357   {
10358     TYPE_SWITCH3,
10359     &setup.override_level_music,                "override_level_music"
10360   },
10361   {
10362     TYPE_INTEGER,
10363     &setup.volume_simple,                       "volume_simple"
10364   },
10365   {
10366     TYPE_INTEGER,
10367     &setup.volume_loops,                        "volume_loops"
10368   },
10369   {
10370     TYPE_INTEGER,
10371     &setup.volume_music,                        "volume_music"
10372   },
10373   {
10374     TYPE_SWITCH,
10375     &setup.network_mode,                        "network_mode"
10376   },
10377   {
10378     TYPE_PLAYER,
10379     &setup.network_player_nr,                   "network_player"
10380   },
10381   {
10382     TYPE_STRING,
10383     &setup.network_server_hostname,             "network_server_hostname"
10384   },
10385   {
10386     TYPE_STRING,
10387     &setup.touch.control_type,                  "touch.control_type"
10388   },
10389   {
10390     TYPE_INTEGER,
10391     &setup.touch.move_distance,                 "touch.move_distance"
10392   },
10393   {
10394     TYPE_INTEGER,
10395     &setup.touch.drop_distance,                 "touch.drop_distance"
10396   },
10397   {
10398     TYPE_INTEGER,
10399     &setup.touch.transparency,                  "touch.transparency"
10400   },
10401   {
10402     TYPE_INTEGER,
10403     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10404   },
10405   {
10406     TYPE_INTEGER,
10407     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10408   },
10409   {
10410     TYPE_INTEGER,
10411     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10412   },
10413   {
10414     TYPE_INTEGER,
10415     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10416   },
10417   {
10418     TYPE_INTEGER,
10419     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10420   },
10421   {
10422     TYPE_INTEGER,
10423     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10424   },
10425   {
10426     TYPE_SWITCH,
10427     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10428   },
10429 };
10430
10431 static struct TokenInfo auto_setup_tokens[] =
10432 {
10433   {
10434     TYPE_INTEGER,
10435     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10436   },
10437 };
10438
10439 static struct TokenInfo server_setup_tokens[] =
10440 {
10441   {
10442     TYPE_STRING,
10443     &setup.player_uuid,                         "player_uuid"
10444   },
10445   {
10446     TYPE_INTEGER,
10447     &setup.player_version,                      "player_version"
10448   },
10449   {
10450     TYPE_SWITCH,
10451     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10452   },
10453   {
10454     TYPE_STRING,
10455     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10456   },
10457   {
10458     TYPE_STRING,
10459     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10460   },
10461   {
10462     TYPE_SWITCH,
10463     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10464   },
10465   {
10466     TYPE_SWITCH,
10467     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10468   },
10469   {
10470     TYPE_SWITCH,
10471     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10472   },
10473   {
10474     TYPE_SWITCH,
10475     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10476   },
10477   {
10478     TYPE_SWITCH,
10479     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10480   },
10481 };
10482
10483 static struct TokenInfo editor_setup_tokens[] =
10484 {
10485   {
10486     TYPE_SWITCH,
10487     &setup.editor.el_classic,                   "editor.el_classic"
10488   },
10489   {
10490     TYPE_SWITCH,
10491     &setup.editor.el_custom,                    "editor.el_custom"
10492   },
10493   {
10494     TYPE_SWITCH,
10495     &setup.editor.el_user_defined,              "editor.el_user_defined"
10496   },
10497   {
10498     TYPE_SWITCH,
10499     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10500   },
10501   {
10502     TYPE_SWITCH,
10503     &setup.editor.el_headlines,                 "editor.el_headlines"
10504   },
10505   {
10506     TYPE_SWITCH,
10507     &setup.editor.show_element_token,           "editor.show_element_token"
10508   },
10509   {
10510     TYPE_SWITCH,
10511     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10512   },
10513 };
10514
10515 static struct TokenInfo editor_cascade_setup_tokens[] =
10516 {
10517   {
10518     TYPE_SWITCH,
10519     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10520   },
10521   {
10522     TYPE_SWITCH,
10523     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10524   },
10525   {
10526     TYPE_SWITCH,
10527     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10528   },
10529   {
10530     TYPE_SWITCH,
10531     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10532   },
10533   {
10534     TYPE_SWITCH,
10535     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10536   },
10537   {
10538     TYPE_SWITCH,
10539     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10540   },
10541   {
10542     TYPE_SWITCH,
10543     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10544   },
10545   {
10546     TYPE_SWITCH,
10547     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10548   },
10549   {
10550     TYPE_SWITCH,
10551     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10552   },
10553   {
10554     TYPE_SWITCH,
10555     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10556   },
10557   {
10558     TYPE_SWITCH,
10559     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10560   },
10561   {
10562     TYPE_SWITCH,
10563     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10564   },
10565   {
10566     TYPE_SWITCH,
10567     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10568   },
10569   {
10570     TYPE_SWITCH,
10571     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10572   },
10573   {
10574     TYPE_SWITCH,
10575     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10576   },
10577   {
10578     TYPE_SWITCH,
10579     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10580   },
10581   {
10582     TYPE_SWITCH,
10583     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10584   },
10585   {
10586     TYPE_SWITCH,
10587     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10588   },
10589   {
10590     TYPE_SWITCH,
10591     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10592   },
10593 };
10594
10595 static struct TokenInfo shortcut_setup_tokens[] =
10596 {
10597   {
10598     TYPE_KEY_X11,
10599     &setup.shortcut.save_game,                  "shortcut.save_game"
10600   },
10601   {
10602     TYPE_KEY_X11,
10603     &setup.shortcut.load_game,                  "shortcut.load_game"
10604   },
10605   {
10606     TYPE_KEY_X11,
10607     &setup.shortcut.restart_game,               "shortcut.restart_game"
10608   },
10609   {
10610     TYPE_KEY_X11,
10611     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10612   },
10613   {
10614     TYPE_KEY_X11,
10615     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10616   },
10617   {
10618     TYPE_KEY_X11,
10619     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10620   },
10621   {
10622     TYPE_KEY_X11,
10623     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10624   },
10625   {
10626     TYPE_KEY_X11,
10627     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10628   },
10629   {
10630     TYPE_KEY_X11,
10631     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10632   },
10633   {
10634     TYPE_KEY_X11,
10635     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10636   },
10637   {
10638     TYPE_KEY_X11,
10639     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10640   },
10641   {
10642     TYPE_KEY_X11,
10643     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10644   },
10645   {
10646     TYPE_KEY_X11,
10647     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10648   },
10649   {
10650     TYPE_KEY_X11,
10651     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10652   },
10653   {
10654     TYPE_KEY_X11,
10655     &setup.shortcut.tape_record,                "shortcut.tape_record"
10656   },
10657   {
10658     TYPE_KEY_X11,
10659     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10660   },
10661   {
10662     TYPE_KEY_X11,
10663     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10664   },
10665   {
10666     TYPE_KEY_X11,
10667     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10668   },
10669   {
10670     TYPE_KEY_X11,
10671     &setup.shortcut.sound_music,                "shortcut.sound_music"
10672   },
10673   {
10674     TYPE_KEY_X11,
10675     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10676   },
10677   {
10678     TYPE_KEY_X11,
10679     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10680   },
10681   {
10682     TYPE_KEY_X11,
10683     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10684   },
10685   {
10686     TYPE_KEY_X11,
10687     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10688   },
10689 };
10690
10691 static struct SetupInputInfo setup_input;
10692 static struct TokenInfo player_setup_tokens[] =
10693 {
10694   {
10695     TYPE_BOOLEAN,
10696     &setup_input.use_joystick,                  ".use_joystick"
10697   },
10698   {
10699     TYPE_STRING,
10700     &setup_input.joy.device_name,               ".joy.device_name"
10701   },
10702   {
10703     TYPE_INTEGER,
10704     &setup_input.joy.xleft,                     ".joy.xleft"
10705   },
10706   {
10707     TYPE_INTEGER,
10708     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10709   },
10710   {
10711     TYPE_INTEGER,
10712     &setup_input.joy.xright,                    ".joy.xright"
10713   },
10714   {
10715     TYPE_INTEGER,
10716     &setup_input.joy.yupper,                    ".joy.yupper"
10717   },
10718   {
10719     TYPE_INTEGER,
10720     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10721   },
10722   {
10723     TYPE_INTEGER,
10724     &setup_input.joy.ylower,                    ".joy.ylower"
10725   },
10726   {
10727     TYPE_INTEGER,
10728     &setup_input.joy.snap,                      ".joy.snap_field"
10729   },
10730   {
10731     TYPE_INTEGER,
10732     &setup_input.joy.drop,                      ".joy.place_bomb"
10733   },
10734   {
10735     TYPE_KEY_X11,
10736     &setup_input.key.left,                      ".key.move_left"
10737   },
10738   {
10739     TYPE_KEY_X11,
10740     &setup_input.key.right,                     ".key.move_right"
10741   },
10742   {
10743     TYPE_KEY_X11,
10744     &setup_input.key.up,                        ".key.move_up"
10745   },
10746   {
10747     TYPE_KEY_X11,
10748     &setup_input.key.down,                      ".key.move_down"
10749   },
10750   {
10751     TYPE_KEY_X11,
10752     &setup_input.key.snap,                      ".key.snap_field"
10753   },
10754   {
10755     TYPE_KEY_X11,
10756     &setup_input.key.drop,                      ".key.place_bomb"
10757   },
10758 };
10759
10760 static struct TokenInfo system_setup_tokens[] =
10761 {
10762   {
10763     TYPE_STRING,
10764     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10765   },
10766   {
10767     TYPE_STRING,
10768     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10769   },
10770   {
10771     TYPE_STRING,
10772     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10773   },
10774   {
10775     TYPE_INTEGER,
10776     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10777   },
10778 };
10779
10780 static struct TokenInfo internal_setup_tokens[] =
10781 {
10782   {
10783     TYPE_STRING,
10784     &setup.internal.program_title,              "program_title"
10785   },
10786   {
10787     TYPE_STRING,
10788     &setup.internal.program_version,            "program_version"
10789   },
10790   {
10791     TYPE_STRING,
10792     &setup.internal.program_author,             "program_author"
10793   },
10794   {
10795     TYPE_STRING,
10796     &setup.internal.program_email,              "program_email"
10797   },
10798   {
10799     TYPE_STRING,
10800     &setup.internal.program_website,            "program_website"
10801   },
10802   {
10803     TYPE_STRING,
10804     &setup.internal.program_copyright,          "program_copyright"
10805   },
10806   {
10807     TYPE_STRING,
10808     &setup.internal.program_company,            "program_company"
10809   },
10810   {
10811     TYPE_STRING,
10812     &setup.internal.program_icon_file,          "program_icon_file"
10813   },
10814   {
10815     TYPE_STRING,
10816     &setup.internal.default_graphics_set,       "default_graphics_set"
10817   },
10818   {
10819     TYPE_STRING,
10820     &setup.internal.default_sounds_set,         "default_sounds_set"
10821   },
10822   {
10823     TYPE_STRING,
10824     &setup.internal.default_music_set,          "default_music_set"
10825   },
10826   {
10827     TYPE_STRING,
10828     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10829   },
10830   {
10831     TYPE_STRING,
10832     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10833   },
10834   {
10835     TYPE_STRING,
10836     &setup.internal.fallback_music_file,        "fallback_music_file"
10837   },
10838   {
10839     TYPE_STRING,
10840     &setup.internal.default_level_series,       "default_level_series"
10841   },
10842   {
10843     TYPE_INTEGER,
10844     &setup.internal.default_window_width,       "default_window_width"
10845   },
10846   {
10847     TYPE_INTEGER,
10848     &setup.internal.default_window_height,      "default_window_height"
10849   },
10850   {
10851     TYPE_BOOLEAN,
10852     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10853   },
10854   {
10855     TYPE_BOOLEAN,
10856     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10857   },
10858   {
10859     TYPE_BOOLEAN,
10860     &setup.internal.create_user_levelset,       "create_user_levelset"
10861   },
10862   {
10863     TYPE_BOOLEAN,
10864     &setup.internal.info_screens_from_main,     "info_screens_from_main"
10865   },
10866   {
10867     TYPE_BOOLEAN,
10868     &setup.internal.menu_game,                  "menu_game"
10869   },
10870   {
10871     TYPE_BOOLEAN,
10872     &setup.internal.menu_engines,               "menu_engines"
10873   },
10874   {
10875     TYPE_BOOLEAN,
10876     &setup.internal.menu_editor,                "menu_editor"
10877   },
10878   {
10879     TYPE_BOOLEAN,
10880     &setup.internal.menu_graphics,              "menu_graphics"
10881   },
10882   {
10883     TYPE_BOOLEAN,
10884     &setup.internal.menu_sound,                 "menu_sound"
10885   },
10886   {
10887     TYPE_BOOLEAN,
10888     &setup.internal.menu_artwork,               "menu_artwork"
10889   },
10890   {
10891     TYPE_BOOLEAN,
10892     &setup.internal.menu_input,                 "menu_input"
10893   },
10894   {
10895     TYPE_BOOLEAN,
10896     &setup.internal.menu_touch,                 "menu_touch"
10897   },
10898   {
10899     TYPE_BOOLEAN,
10900     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10901   },
10902   {
10903     TYPE_BOOLEAN,
10904     &setup.internal.menu_exit,                  "menu_exit"
10905   },
10906   {
10907     TYPE_BOOLEAN,
10908     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10909   },
10910   {
10911     TYPE_BOOLEAN,
10912     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
10913   },
10914   {
10915     TYPE_BOOLEAN,
10916     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
10917   },
10918   {
10919     TYPE_BOOLEAN,
10920     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
10921   },
10922   {
10923     TYPE_BOOLEAN,
10924     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
10925   },
10926   {
10927     TYPE_BOOLEAN,
10928     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
10929   },
10930   {
10931     TYPE_BOOLEAN,
10932     &setup.internal.info_title,                 "info_title"
10933   },
10934   {
10935     TYPE_BOOLEAN,
10936     &setup.internal.info_elements,              "info_elements"
10937   },
10938   {
10939     TYPE_BOOLEAN,
10940     &setup.internal.info_music,                 "info_music"
10941   },
10942   {
10943     TYPE_BOOLEAN,
10944     &setup.internal.info_credits,               "info_credits"
10945   },
10946   {
10947     TYPE_BOOLEAN,
10948     &setup.internal.info_program,               "info_program"
10949   },
10950   {
10951     TYPE_BOOLEAN,
10952     &setup.internal.info_version,               "info_version"
10953   },
10954   {
10955     TYPE_BOOLEAN,
10956     &setup.internal.info_levelset,              "info_levelset"
10957   },
10958   {
10959     TYPE_BOOLEAN,
10960     &setup.internal.info_exit,                  "info_exit"
10961   },
10962 };
10963
10964 static struct TokenInfo debug_setup_tokens[] =
10965 {
10966   {
10967     TYPE_INTEGER,
10968     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10969   },
10970   {
10971     TYPE_INTEGER,
10972     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10973   },
10974   {
10975     TYPE_INTEGER,
10976     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10977   },
10978   {
10979     TYPE_INTEGER,
10980     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10981   },
10982   {
10983     TYPE_INTEGER,
10984     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10985   },
10986   {
10987     TYPE_INTEGER,
10988     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
10989   },
10990   {
10991     TYPE_INTEGER,
10992     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
10993   },
10994   {
10995     TYPE_INTEGER,
10996     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
10997   },
10998   {
10999     TYPE_INTEGER,
11000     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11001   },
11002   {
11003     TYPE_INTEGER,
11004     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11005   },
11006   {
11007     TYPE_KEY_X11,
11008     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11009   },
11010   {
11011     TYPE_KEY_X11,
11012     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11013   },
11014   {
11015     TYPE_KEY_X11,
11016     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11017   },
11018   {
11019     TYPE_KEY_X11,
11020     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11021   },
11022   {
11023     TYPE_KEY_X11,
11024     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11025   },
11026   {
11027     TYPE_KEY_X11,
11028     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11029   },
11030   {
11031     TYPE_KEY_X11,
11032     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11033   },
11034   {
11035     TYPE_KEY_X11,
11036     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11037   },
11038   {
11039     TYPE_KEY_X11,
11040     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11041   },
11042   {
11043     TYPE_KEY_X11,
11044     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11045   },
11046   {
11047     TYPE_BOOLEAN,
11048     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11049   {
11050     TYPE_BOOLEAN,
11051     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11052   },
11053   {
11054     TYPE_BOOLEAN,
11055     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11056   },
11057   {
11058     TYPE_SWITCH3,
11059     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11060   },
11061   {
11062     TYPE_INTEGER,
11063     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11064   },
11065 };
11066
11067 static struct TokenInfo options_setup_tokens[] =
11068 {
11069   {
11070     TYPE_BOOLEAN,
11071     &setup.options.verbose,                     "options.verbose"
11072   },
11073   {
11074     TYPE_BOOLEAN,
11075     &setup.options.debug,                       "options.debug"
11076   },
11077   {
11078     TYPE_STRING,
11079     &setup.options.debug_mode,                  "options.debug_mode"
11080   },
11081 };
11082
11083 static void setSetupInfoToDefaults(struct SetupInfo *si)
11084 {
11085   int i;
11086
11087   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11088
11089   si->multiple_users = TRUE;
11090
11091   si->sound = TRUE;
11092   si->sound_loops = TRUE;
11093   si->sound_music = TRUE;
11094   si->sound_simple = TRUE;
11095   si->toons = TRUE;
11096   si->global_animations = TRUE;
11097   si->scroll_delay = TRUE;
11098   si->forced_scroll_delay = FALSE;
11099   si->scroll_delay_value = STD_SCROLL_DELAY;
11100   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11101   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11102   si->fade_screens = TRUE;
11103   si->autorecord = TRUE;
11104   si->autorecord_after_replay = TRUE;
11105   si->auto_pause_on_start = FALSE;
11106   si->show_titlescreen = TRUE;
11107   si->quick_doors = FALSE;
11108   si->team_mode = FALSE;
11109   si->handicap = TRUE;
11110   si->skip_levels = TRUE;
11111   si->increment_levels = TRUE;
11112   si->auto_play_next_level = TRUE;
11113   si->count_score_after_game = TRUE;
11114   si->show_scores_after_game = TRUE;
11115   si->time_limit = TRUE;
11116   si->fullscreen = FALSE;
11117   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11118   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11119   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11120   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11121   si->ask_on_escape = TRUE;
11122   si->ask_on_escape_editor = TRUE;
11123   si->ask_on_game_over = TRUE;
11124   si->ask_on_quit_game = TRUE;
11125   si->ask_on_quit_program = TRUE;
11126   si->quick_switch = FALSE;
11127   si->input_on_focus = FALSE;
11128   si->prefer_aga_graphics = TRUE;
11129   si->prefer_lowpass_sounds = FALSE;
11130   si->prefer_extra_panel_items = TRUE;
11131   si->game_speed_extended = FALSE;
11132   si->game_frame_delay = GAME_FRAME_DELAY;
11133   si->bd_skip_uncovering = FALSE;
11134   si->bd_skip_hatching = FALSE;
11135   si->bd_scroll_delay = TRUE;
11136   si->bd_smooth_movements = AUTO;
11137   si->sp_show_border_elements = FALSE;
11138   si->small_game_graphics = FALSE;
11139   si->show_load_save_buttons = FALSE;
11140   si->show_undo_redo_buttons = FALSE;
11141   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11142
11143   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11144   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11145   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11146
11147   si->override_level_graphics = FALSE;
11148   si->override_level_sounds = FALSE;
11149   si->override_level_music = FALSE;
11150
11151   si->volume_simple = 100;              // percent
11152   si->volume_loops = 100;               // percent
11153   si->volume_music = 100;               // percent
11154
11155   si->network_mode = FALSE;
11156   si->network_player_nr = 0;            // first player
11157   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11158
11159   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11160   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11161   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11162   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11163   si->touch.draw_outlined = TRUE;
11164   si->touch.draw_pressed = TRUE;
11165
11166   for (i = 0; i < 2; i++)
11167   {
11168     char *default_grid_button[6][2] =
11169     {
11170       { "      ", "  ^^  " },
11171       { "      ", "  ^^  " },
11172       { "      ", "<<  >>" },
11173       { "      ", "<<  >>" },
11174       { "111222", "  vv  " },
11175       { "111222", "  vv  " }
11176     };
11177     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11178     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11179     int min_xsize = MIN(6, grid_xsize);
11180     int min_ysize = MIN(6, grid_ysize);
11181     int startx = grid_xsize - min_xsize;
11182     int starty = grid_ysize - min_ysize;
11183     int x, y;
11184
11185     // virtual buttons grid can only be set to defaults if video is initialized
11186     // (this will be repeated if virtual buttons are not loaded from setup file)
11187     if (video.initialized)
11188     {
11189       si->touch.grid_xsize[i] = grid_xsize;
11190       si->touch.grid_ysize[i] = grid_ysize;
11191     }
11192     else
11193     {
11194       si->touch.grid_xsize[i] = -1;
11195       si->touch.grid_ysize[i] = -1;
11196     }
11197
11198     for (x = 0; x < MAX_GRID_XSIZE; x++)
11199       for (y = 0; y < MAX_GRID_YSIZE; y++)
11200         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11201
11202     for (x = 0; x < min_xsize; x++)
11203       for (y = 0; y < min_ysize; y++)
11204         si->touch.grid_button[i][x][starty + y] =
11205           default_grid_button[y][0][x];
11206
11207     for (x = 0; x < min_xsize; x++)
11208       for (y = 0; y < min_ysize; y++)
11209         si->touch.grid_button[i][startx + x][starty + y] =
11210           default_grid_button[y][1][x];
11211   }
11212
11213   si->touch.grid_initialized            = video.initialized;
11214
11215   si->touch.overlay_buttons             = FALSE;
11216
11217   si->editor.el_boulderdash             = TRUE;
11218   si->editor.el_boulderdash_native      = TRUE;
11219   si->editor.el_emerald_mine            = TRUE;
11220   si->editor.el_emerald_mine_club       = TRUE;
11221   si->editor.el_more                    = TRUE;
11222   si->editor.el_sokoban                 = TRUE;
11223   si->editor.el_supaplex                = TRUE;
11224   si->editor.el_diamond_caves           = TRUE;
11225   si->editor.el_dx_boulderdash          = TRUE;
11226
11227   si->editor.el_mirror_magic            = TRUE;
11228   si->editor.el_deflektor               = TRUE;
11229
11230   si->editor.el_chars                   = TRUE;
11231   si->editor.el_steel_chars             = TRUE;
11232
11233   si->editor.el_classic                 = TRUE;
11234   si->editor.el_custom                  = TRUE;
11235
11236   si->editor.el_user_defined            = FALSE;
11237   si->editor.el_dynamic                 = TRUE;
11238
11239   si->editor.el_headlines               = TRUE;
11240
11241   si->editor.show_element_token         = FALSE;
11242
11243   si->editor.show_read_only_warning     = TRUE;
11244
11245   si->editor.use_template_for_new_levels = TRUE;
11246
11247   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11248   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11249   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11250   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11251   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11252
11253   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11254   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11255   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11256   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11257   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11258
11259   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11260   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11261   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11262   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11263   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11264   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11265
11266   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11267   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11268   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11269
11270   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11271   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11272   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11273   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11274
11275   for (i = 0; i < MAX_PLAYERS; i++)
11276   {
11277     si->input[i].use_joystick = FALSE;
11278     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11279     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11280     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11281     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11282     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11283     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11284     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11285     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11286     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11287     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11288     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11289     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11290     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11291     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11292     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11293   }
11294
11295   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11296   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11297   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11298   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11299
11300   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11301   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11302   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11303   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11304   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11305   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11306   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11307
11308   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11309
11310   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11311   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11312   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11313
11314   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11315   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11316   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11317
11318   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11319   si->internal.choose_from_top_leveldir = FALSE;
11320   si->internal.show_scaling_in_title = TRUE;
11321   si->internal.create_user_levelset = TRUE;
11322   si->internal.info_screens_from_main = FALSE;
11323
11324   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11325   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11326
11327   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11328   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11329   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11330   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11331   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11332   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11333   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11334   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11335   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11336   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11337
11338   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11339   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11340   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11341   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11342   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11343   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11344   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11345   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11346   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11347   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11348
11349   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11350   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11351
11352   si->debug.show_frames_per_second = FALSE;
11353
11354   si->debug.xsn_mode = AUTO;
11355   si->debug.xsn_percent = 0;
11356
11357   si->options.verbose = FALSE;
11358   si->options.debug = FALSE;
11359   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11360
11361 #if defined(PLATFORM_ANDROID)
11362   si->fullscreen = TRUE;
11363   si->touch.overlay_buttons = TRUE;
11364 #endif
11365
11366   setHideSetupEntry(&setup.debug.xsn_mode);
11367 }
11368
11369 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11370 {
11371   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11372 }
11373
11374 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11375 {
11376   si->player_uuid = NULL;       // (will be set later)
11377   si->player_version = 1;       // (will be set later)
11378
11379   si->use_api_server = TRUE;
11380   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11381   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11382   si->ask_for_uploading_tapes = TRUE;
11383   si->ask_for_remaining_tapes = FALSE;
11384   si->provide_uploading_tapes = TRUE;
11385   si->ask_for_using_api_server = TRUE;
11386   si->has_remaining_tapes = FALSE;
11387 }
11388
11389 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11390 {
11391   si->editor_cascade.el_bd              = TRUE;
11392   si->editor_cascade.el_bd_native       = TRUE;
11393   si->editor_cascade.el_em              = TRUE;
11394   si->editor_cascade.el_emc             = TRUE;
11395   si->editor_cascade.el_rnd             = TRUE;
11396   si->editor_cascade.el_sb              = TRUE;
11397   si->editor_cascade.el_sp              = TRUE;
11398   si->editor_cascade.el_dc              = TRUE;
11399   si->editor_cascade.el_dx              = TRUE;
11400
11401   si->editor_cascade.el_mm              = TRUE;
11402   si->editor_cascade.el_df              = TRUE;
11403
11404   si->editor_cascade.el_chars           = FALSE;
11405   si->editor_cascade.el_steel_chars     = FALSE;
11406   si->editor_cascade.el_ce              = FALSE;
11407   si->editor_cascade.el_ge              = FALSE;
11408   si->editor_cascade.el_es              = FALSE;
11409   si->editor_cascade.el_ref             = FALSE;
11410   si->editor_cascade.el_user            = FALSE;
11411   si->editor_cascade.el_dynamic         = FALSE;
11412 }
11413
11414 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11415
11416 static char *getHideSetupToken(void *setup_value)
11417 {
11418   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11419
11420   if (setup_value != NULL)
11421     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11422
11423   return hide_setup_token;
11424 }
11425
11426 void setHideSetupEntry(void *setup_value)
11427 {
11428   char *hide_setup_token = getHideSetupToken(setup_value);
11429
11430   if (hide_setup_hash == NULL)
11431     hide_setup_hash = newSetupFileHash();
11432
11433   if (setup_value != NULL)
11434     setHashEntry(hide_setup_hash, hide_setup_token, "");
11435 }
11436
11437 void removeHideSetupEntry(void *setup_value)
11438 {
11439   char *hide_setup_token = getHideSetupToken(setup_value);
11440
11441   if (setup_value != NULL)
11442     removeHashEntry(hide_setup_hash, hide_setup_token);
11443 }
11444
11445 boolean hideSetupEntry(void *setup_value)
11446 {
11447   char *hide_setup_token = getHideSetupToken(setup_value);
11448
11449   return (setup_value != NULL &&
11450           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11451 }
11452
11453 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11454                                       struct TokenInfo *token_info,
11455                                       int token_nr, char *token_text)
11456 {
11457   char *token_hide_text = getStringCat2(token_text, ".hide");
11458   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11459
11460   // set the value of this setup option in the setup option structure
11461   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11462
11463   // check if this setup option should be hidden in the setup menu
11464   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11465     setHideSetupEntry(token_info[token_nr].value);
11466
11467   free(token_hide_text);
11468 }
11469
11470 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11471                                       struct TokenInfo *token_info,
11472                                       int token_nr)
11473 {
11474   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11475                             token_info[token_nr].text);
11476 }
11477
11478 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11479 {
11480   int i, pnr;
11481
11482   if (!setup_file_hash)
11483     return;
11484
11485   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11486     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11487
11488   setup.touch.grid_initialized = TRUE;
11489   for (i = 0; i < 2; i++)
11490   {
11491     int grid_xsize = setup.touch.grid_xsize[i];
11492     int grid_ysize = setup.touch.grid_ysize[i];
11493     int x, y;
11494
11495     // if virtual buttons are not loaded from setup file, repeat initializing
11496     // virtual buttons grid with default values later when video is initialized
11497     if (grid_xsize == -1 ||
11498         grid_ysize == -1)
11499     {
11500       setup.touch.grid_initialized = FALSE;
11501
11502       continue;
11503     }
11504
11505     for (y = 0; y < grid_ysize; y++)
11506     {
11507       char token_string[MAX_LINE_LEN];
11508
11509       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11510
11511       char *value_string = getHashEntry(setup_file_hash, token_string);
11512
11513       if (value_string == NULL)
11514         continue;
11515
11516       for (x = 0; x < grid_xsize; x++)
11517       {
11518         char c = value_string[x];
11519
11520         setup.touch.grid_button[i][x][y] =
11521           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11522       }
11523     }
11524   }
11525
11526   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11527     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11528
11529   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11530     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11531
11532   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11533   {
11534     char prefix[30];
11535
11536     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11537
11538     setup_input = setup.input[pnr];
11539     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11540     {
11541       char full_token[100];
11542
11543       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11544       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11545                                 full_token);
11546     }
11547     setup.input[pnr] = setup_input;
11548   }
11549
11550   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11551     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11552
11553   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11554     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11555
11556   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11557     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11558
11559   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11560     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11561
11562   setHideRelatedSetupEntries();
11563 }
11564
11565 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11566 {
11567   int i;
11568
11569   if (!setup_file_hash)
11570     return;
11571
11572   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11573     setSetupInfo(auto_setup_tokens, i,
11574                  getHashEntry(setup_file_hash,
11575                               auto_setup_tokens[i].text));
11576 }
11577
11578 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11579 {
11580   int i;
11581
11582   if (!setup_file_hash)
11583     return;
11584
11585   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11586     setSetupInfo(server_setup_tokens, i,
11587                  getHashEntry(setup_file_hash,
11588                               server_setup_tokens[i].text));
11589 }
11590
11591 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11592 {
11593   int i;
11594
11595   if (!setup_file_hash)
11596     return;
11597
11598   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11599     setSetupInfo(editor_cascade_setup_tokens, i,
11600                  getHashEntry(setup_file_hash,
11601                               editor_cascade_setup_tokens[i].text));
11602 }
11603
11604 void LoadUserNames(void)
11605 {
11606   int last_user_nr = user.nr;
11607   int i;
11608
11609   if (global.user_names != NULL)
11610   {
11611     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11612       checked_free(global.user_names[i]);
11613
11614     checked_free(global.user_names);
11615   }
11616
11617   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11618
11619   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11620   {
11621     user.nr = i;
11622
11623     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11624
11625     if (setup_file_hash)
11626     {
11627       char *player_name = getHashEntry(setup_file_hash, "player_name");
11628
11629       global.user_names[i] = getFixedUserName(player_name);
11630
11631       freeSetupFileHash(setup_file_hash);
11632     }
11633
11634     if (global.user_names[i] == NULL)
11635       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11636   }
11637
11638   user.nr = last_user_nr;
11639 }
11640
11641 void LoadSetupFromFilename(char *filename)
11642 {
11643   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11644
11645   if (setup_file_hash)
11646   {
11647     decodeSetupFileHash_Default(setup_file_hash);
11648
11649     freeSetupFileHash(setup_file_hash);
11650   }
11651   else
11652   {
11653     Debug("setup", "using default setup values");
11654   }
11655 }
11656
11657 static void LoadSetup_SpecialPostProcessing(void)
11658 {
11659   char *player_name_new;
11660
11661   // needed to work around problems with fixed length strings
11662   player_name_new = getFixedUserName(setup.player_name);
11663   free(setup.player_name);
11664   setup.player_name = player_name_new;
11665
11666   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11667   if (setup.scroll_delay == FALSE)
11668   {
11669     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11670     setup.scroll_delay = TRUE;                  // now always "on"
11671   }
11672
11673   // make sure that scroll delay value stays inside valid range
11674   setup.scroll_delay_value =
11675     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11676 }
11677
11678 void LoadSetup_Default(void)
11679 {
11680   char *filename;
11681
11682   // always start with reliable default values
11683   setSetupInfoToDefaults(&setup);
11684
11685   // try to load setup values from default setup file
11686   filename = getDefaultSetupFilename();
11687
11688   if (fileExists(filename))
11689     LoadSetupFromFilename(filename);
11690
11691   // try to load setup values from platform setup file
11692   filename = getPlatformSetupFilename();
11693
11694   if (fileExists(filename))
11695     LoadSetupFromFilename(filename);
11696
11697   // try to load setup values from user setup file
11698   filename = getSetupFilename();
11699
11700   LoadSetupFromFilename(filename);
11701
11702   LoadSetup_SpecialPostProcessing();
11703 }
11704
11705 void LoadSetup_AutoSetup(void)
11706 {
11707   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11708   SetupFileHash *setup_file_hash = NULL;
11709
11710   // always start with reliable default values
11711   setSetupInfoToDefaults_AutoSetup(&setup);
11712
11713   setup_file_hash = loadSetupFileHash(filename);
11714
11715   if (setup_file_hash)
11716   {
11717     decodeSetupFileHash_AutoSetup(setup_file_hash);
11718
11719     freeSetupFileHash(setup_file_hash);
11720   }
11721
11722   free(filename);
11723 }
11724
11725 void LoadSetup_ServerSetup(void)
11726 {
11727   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11728   SetupFileHash *setup_file_hash = NULL;
11729
11730   // always start with reliable default values
11731   setSetupInfoToDefaults_ServerSetup(&setup);
11732
11733   setup_file_hash = loadSetupFileHash(filename);
11734
11735   if (setup_file_hash)
11736   {
11737     decodeSetupFileHash_ServerSetup(setup_file_hash);
11738
11739     freeSetupFileHash(setup_file_hash);
11740   }
11741
11742   free(filename);
11743
11744   if (setup.player_uuid == NULL)
11745   {
11746     // player UUID does not yet exist in setup file
11747     setup.player_uuid = getStringCopy(getUUID());
11748     setup.player_version = 2;
11749
11750     SaveSetup_ServerSetup();
11751   }
11752 }
11753
11754 void LoadSetup_EditorCascade(void)
11755 {
11756   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11757   SetupFileHash *setup_file_hash = NULL;
11758
11759   // always start with reliable default values
11760   setSetupInfoToDefaults_EditorCascade(&setup);
11761
11762   setup_file_hash = loadSetupFileHash(filename);
11763
11764   if (setup_file_hash)
11765   {
11766     decodeSetupFileHash_EditorCascade(setup_file_hash);
11767
11768     freeSetupFileHash(setup_file_hash);
11769   }
11770
11771   free(filename);
11772 }
11773
11774 void LoadSetup(void)
11775 {
11776   LoadSetup_Default();
11777   LoadSetup_AutoSetup();
11778   LoadSetup_ServerSetup();
11779   LoadSetup_EditorCascade();
11780 }
11781
11782 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11783                                            char *mapping_line)
11784 {
11785   char mapping_guid[MAX_LINE_LEN];
11786   char *mapping_start, *mapping_end;
11787
11788   // get GUID from game controller mapping line: copy complete line
11789   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11790   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11791
11792   // get GUID from game controller mapping line: cut after GUID part
11793   mapping_start = strchr(mapping_guid, ',');
11794   if (mapping_start != NULL)
11795     *mapping_start = '\0';
11796
11797   // cut newline from game controller mapping line
11798   mapping_end = strchr(mapping_line, '\n');
11799   if (mapping_end != NULL)
11800     *mapping_end = '\0';
11801
11802   // add mapping entry to game controller mappings hash
11803   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11804 }
11805
11806 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11807                                                  char *filename)
11808 {
11809   FILE *file;
11810
11811   if (!(file = fopen(filename, MODE_READ)))
11812   {
11813     Warn("cannot read game controller mappings file '%s'", filename);
11814
11815     return;
11816   }
11817
11818   while (!feof(file))
11819   {
11820     char line[MAX_LINE_LEN];
11821
11822     if (!fgets(line, MAX_LINE_LEN, file))
11823       break;
11824
11825     addGameControllerMappingToHash(mappings_hash, line);
11826   }
11827
11828   fclose(file);
11829 }
11830
11831 void SaveSetup_Default(void)
11832 {
11833   char *filename = getSetupFilename();
11834   FILE *file;
11835   int i, pnr;
11836
11837   InitUserDataDirectory();
11838
11839   if (!(file = fopen(filename, MODE_WRITE)))
11840   {
11841     Warn("cannot write setup file '%s'", filename);
11842
11843     return;
11844   }
11845
11846   fprintFileHeader(file, SETUP_FILENAME);
11847
11848   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11849   {
11850     // just to make things nicer :)
11851     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11852         global_setup_tokens[i].value == &setup.sound                    ||
11853         global_setup_tokens[i].value == &setup.graphics_set             ||
11854         global_setup_tokens[i].value == &setup.volume_simple            ||
11855         global_setup_tokens[i].value == &setup.network_mode             ||
11856         global_setup_tokens[i].value == &setup.touch.control_type       ||
11857         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11858         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11859       fprintf(file, "\n");
11860
11861     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11862   }
11863
11864   for (i = 0; i < 2; i++)
11865   {
11866     int grid_xsize = setup.touch.grid_xsize[i];
11867     int grid_ysize = setup.touch.grid_ysize[i];
11868     int x, y;
11869
11870     fprintf(file, "\n");
11871
11872     for (y = 0; y < grid_ysize; y++)
11873     {
11874       char token_string[MAX_LINE_LEN];
11875       char value_string[MAX_LINE_LEN];
11876
11877       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11878
11879       for (x = 0; x < grid_xsize; x++)
11880       {
11881         char c = setup.touch.grid_button[i][x][y];
11882
11883         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11884       }
11885
11886       value_string[grid_xsize] = '\0';
11887
11888       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11889     }
11890   }
11891
11892   fprintf(file, "\n");
11893   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11894     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11895
11896   fprintf(file, "\n");
11897   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11898     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11899
11900   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11901   {
11902     char prefix[30];
11903
11904     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11905     fprintf(file, "\n");
11906
11907     setup_input = setup.input[pnr];
11908     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11909       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11910   }
11911
11912   fprintf(file, "\n");
11913   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11914     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11915
11916   // (internal setup values not saved to user setup file)
11917
11918   fprintf(file, "\n");
11919   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11920     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11921         setup.debug.xsn_mode != AUTO)
11922       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11923
11924   fprintf(file, "\n");
11925   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11926     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11927
11928   fclose(file);
11929
11930   SetFilePermissions(filename, PERMS_PRIVATE);
11931 }
11932
11933 void SaveSetup_AutoSetup(void)
11934 {
11935   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11936   FILE *file;
11937   int i;
11938
11939   InitUserDataDirectory();
11940
11941   if (!(file = fopen(filename, MODE_WRITE)))
11942   {
11943     Warn("cannot write auto setup file '%s'", filename);
11944
11945     free(filename);
11946
11947     return;
11948   }
11949
11950   fprintFileHeader(file, AUTOSETUP_FILENAME);
11951
11952   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11953     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11954
11955   fclose(file);
11956
11957   SetFilePermissions(filename, PERMS_PRIVATE);
11958
11959   free(filename);
11960 }
11961
11962 void SaveSetup_ServerSetup(void)
11963 {
11964   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11965   FILE *file;
11966   int i;
11967
11968   InitUserDataDirectory();
11969
11970   if (!(file = fopen(filename, MODE_WRITE)))
11971   {
11972     Warn("cannot write server setup file '%s'", filename);
11973
11974     free(filename);
11975
11976     return;
11977   }
11978
11979   fprintFileHeader(file, SERVERSETUP_FILENAME);
11980
11981   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11982   {
11983     // just to make things nicer :)
11984     if (server_setup_tokens[i].value == &setup.use_api_server)
11985       fprintf(file, "\n");
11986
11987     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11988   }
11989
11990   fclose(file);
11991
11992   SetFilePermissions(filename, PERMS_PRIVATE);
11993
11994   free(filename);
11995 }
11996
11997 void SaveSetup_EditorCascade(void)
11998 {
11999   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12000   FILE *file;
12001   int i;
12002
12003   InitUserDataDirectory();
12004
12005   if (!(file = fopen(filename, MODE_WRITE)))
12006   {
12007     Warn("cannot write editor cascade state file '%s'", filename);
12008
12009     free(filename);
12010
12011     return;
12012   }
12013
12014   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12015
12016   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12017     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12018
12019   fclose(file);
12020
12021   SetFilePermissions(filename, PERMS_PRIVATE);
12022
12023   free(filename);
12024 }
12025
12026 void SaveSetup(void)
12027 {
12028   SaveSetup_Default();
12029   SaveSetup_AutoSetup();
12030   SaveSetup_ServerSetup();
12031   SaveSetup_EditorCascade();
12032 }
12033
12034 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12035                                                   char *filename)
12036 {
12037   FILE *file;
12038
12039   if (!(file = fopen(filename, MODE_WRITE)))
12040   {
12041     Warn("cannot write game controller mappings file '%s'", filename);
12042
12043     return;
12044   }
12045
12046   BEGIN_HASH_ITERATION(mappings_hash, itr)
12047   {
12048     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12049   }
12050   END_HASH_ITERATION(mappings_hash, itr)
12051
12052   fclose(file);
12053 }
12054
12055 void SaveSetup_AddGameControllerMapping(char *mapping)
12056 {
12057   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12058   SetupFileHash *mappings_hash = newSetupFileHash();
12059
12060   InitUserDataDirectory();
12061
12062   // load existing personal game controller mappings
12063   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12064
12065   // add new mapping to personal game controller mappings
12066   addGameControllerMappingToHash(mappings_hash, mapping);
12067
12068   // save updated personal game controller mappings
12069   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12070
12071   freeSetupFileHash(mappings_hash);
12072   free(filename);
12073 }
12074
12075 void LoadCustomElementDescriptions(void)
12076 {
12077   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12078   SetupFileHash *setup_file_hash;
12079   int i;
12080
12081   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12082   {
12083     if (element_info[i].custom_description != NULL)
12084     {
12085       free(element_info[i].custom_description);
12086       element_info[i].custom_description = NULL;
12087     }
12088   }
12089
12090   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12091     return;
12092
12093   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12094   {
12095     char *token = getStringCat2(element_info[i].token_name, ".name");
12096     char *value = getHashEntry(setup_file_hash, token);
12097
12098     if (value != NULL)
12099       element_info[i].custom_description = getStringCopy(value);
12100
12101     free(token);
12102   }
12103
12104   freeSetupFileHash(setup_file_hash);
12105 }
12106
12107 static int getElementFromToken(char *token)
12108 {
12109   char *value = getHashEntry(element_token_hash, token);
12110
12111   if (value != NULL)
12112     return atoi(value);
12113
12114   Warn("unknown element token '%s'", token);
12115
12116   return EL_UNDEFINED;
12117 }
12118
12119 void FreeGlobalAnimEventInfo(void)
12120 {
12121   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12122
12123   if (gaei->event_list == NULL)
12124     return;
12125
12126   int i;
12127
12128   for (i = 0; i < gaei->num_event_lists; i++)
12129   {
12130     checked_free(gaei->event_list[i]->event_value);
12131     checked_free(gaei->event_list[i]);
12132   }
12133
12134   checked_free(gaei->event_list);
12135
12136   gaei->event_list = NULL;
12137   gaei->num_event_lists = 0;
12138 }
12139
12140 static int AddGlobalAnimEventList(void)
12141 {
12142   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12143   int list_pos = gaei->num_event_lists++;
12144
12145   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12146                                      sizeof(struct GlobalAnimEventListInfo *));
12147
12148   gaei->event_list[list_pos] =
12149     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12150
12151   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12152
12153   gaeli->event_value = NULL;
12154   gaeli->num_event_values = 0;
12155
12156   return list_pos;
12157 }
12158
12159 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12160 {
12161   // do not add empty global animation events
12162   if (event_value == ANIM_EVENT_NONE)
12163     return list_pos;
12164
12165   // if list position is undefined, create new list
12166   if (list_pos == ANIM_EVENT_UNDEFINED)
12167     list_pos = AddGlobalAnimEventList();
12168
12169   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12170   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12171   int value_pos = gaeli->num_event_values++;
12172
12173   gaeli->event_value = checked_realloc(gaeli->event_value,
12174                                        gaeli->num_event_values * sizeof(int *));
12175
12176   gaeli->event_value[value_pos] = event_value;
12177
12178   return list_pos;
12179 }
12180
12181 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12182 {
12183   if (list_pos == ANIM_EVENT_UNDEFINED)
12184     return ANIM_EVENT_NONE;
12185
12186   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12187   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12188
12189   return gaeli->event_value[value_pos];
12190 }
12191
12192 int GetGlobalAnimEventValueCount(int list_pos)
12193 {
12194   if (list_pos == ANIM_EVENT_UNDEFINED)
12195     return 0;
12196
12197   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12198   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12199
12200   return gaeli->num_event_values;
12201 }
12202
12203 // This function checks if a string <s> of the format "string1, string2, ..."
12204 // exactly contains a string <s_contained>.
12205
12206 static boolean string_has_parameter(char *s, char *s_contained)
12207 {
12208   char *substring;
12209
12210   if (s == NULL || s_contained == NULL)
12211     return FALSE;
12212
12213   if (strlen(s_contained) > strlen(s))
12214     return FALSE;
12215
12216   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12217   {
12218     char next_char = s[strlen(s_contained)];
12219
12220     // check if next character is delimiter or whitespace
12221     if (next_char == ',' || next_char == '\0' ||
12222         next_char == ' ' || next_char == '\t')
12223       return TRUE;
12224   }
12225
12226   // check if string contains another parameter string after a comma
12227   substring = strchr(s, ',');
12228   if (substring == NULL)        // string does not contain a comma
12229     return FALSE;
12230
12231   // advance string pointer to next character after the comma
12232   substring++;
12233
12234   // skip potential whitespaces after the comma
12235   while (*substring == ' ' || *substring == '\t')
12236     substring++;
12237
12238   return string_has_parameter(substring, s_contained);
12239 }
12240
12241 static int get_anim_parameter_value_ce(char *s)
12242 {
12243   char *s_ptr = s;
12244   char *pattern_1 = "ce_change:custom_";
12245   char *pattern_2 = ".page_";
12246   int pattern_1_len = strlen(pattern_1);
12247   char *matching_char = strstr(s_ptr, pattern_1);
12248   int result = ANIM_EVENT_NONE;
12249
12250   if (matching_char == NULL)
12251     return ANIM_EVENT_NONE;
12252
12253   result = ANIM_EVENT_CE_CHANGE;
12254
12255   s_ptr = matching_char + pattern_1_len;
12256
12257   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12258   if (*s_ptr >= '0' && *s_ptr <= '9')
12259   {
12260     int gic_ce_nr = (*s_ptr++ - '0');
12261
12262     if (*s_ptr >= '0' && *s_ptr <= '9')
12263     {
12264       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12265
12266       if (*s_ptr >= '0' && *s_ptr <= '9')
12267         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12268     }
12269
12270     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12271       return ANIM_EVENT_NONE;
12272
12273     // custom element stored as 0 to 255
12274     gic_ce_nr--;
12275
12276     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12277   }
12278   else
12279   {
12280     // invalid custom element number specified
12281
12282     return ANIM_EVENT_NONE;
12283   }
12284
12285   // check for change page number ("page_X" or "page_XX") (optional)
12286   if (strPrefix(s_ptr, pattern_2))
12287   {
12288     s_ptr += strlen(pattern_2);
12289
12290     if (*s_ptr >= '0' && *s_ptr <= '9')
12291     {
12292       int gic_page_nr = (*s_ptr++ - '0');
12293
12294       if (*s_ptr >= '0' && *s_ptr <= '9')
12295         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12296
12297       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12298         return ANIM_EVENT_NONE;
12299
12300       // change page stored as 1 to 32 (0 means "all change pages")
12301
12302       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12303     }
12304     else
12305     {
12306       // invalid animation part number specified
12307
12308       return ANIM_EVENT_NONE;
12309     }
12310   }
12311
12312   // discard result if next character is neither delimiter nor whitespace
12313   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12314         *s_ptr == ' ' || *s_ptr == '\t'))
12315     return ANIM_EVENT_NONE;
12316
12317   return result;
12318 }
12319
12320 static int get_anim_parameter_value(char *s)
12321 {
12322   int event_value[] =
12323   {
12324     ANIM_EVENT_CLICK,
12325     ANIM_EVENT_INIT,
12326     ANIM_EVENT_START,
12327     ANIM_EVENT_END,
12328     ANIM_EVENT_POST
12329   };
12330   char *pattern_1[] =
12331   {
12332     "click:anim_",
12333     "init:anim_",
12334     "start:anim_",
12335     "end:anim_",
12336     "post:anim_"
12337   };
12338   char *pattern_2 = ".part_";
12339   char *matching_char = NULL;
12340   char *s_ptr = s;
12341   int pattern_1_len = 0;
12342   int result = ANIM_EVENT_NONE;
12343   int i;
12344
12345   result = get_anim_parameter_value_ce(s);
12346
12347   if (result != ANIM_EVENT_NONE)
12348     return result;
12349
12350   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12351   {
12352     matching_char = strstr(s_ptr, pattern_1[i]);
12353     pattern_1_len = strlen(pattern_1[i]);
12354     result = event_value[i];
12355
12356     if (matching_char != NULL)
12357       break;
12358   }
12359
12360   if (matching_char == NULL)
12361     return ANIM_EVENT_NONE;
12362
12363   s_ptr = matching_char + pattern_1_len;
12364
12365   // check for main animation number ("anim_X" or "anim_XX")
12366   if (*s_ptr >= '0' && *s_ptr <= '9')
12367   {
12368     int gic_anim_nr = (*s_ptr++ - '0');
12369
12370     if (*s_ptr >= '0' && *s_ptr <= '9')
12371       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12372
12373     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12374       return ANIM_EVENT_NONE;
12375
12376     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12377   }
12378   else
12379   {
12380     // invalid main animation number specified
12381
12382     return ANIM_EVENT_NONE;
12383   }
12384
12385   // check for animation part number ("part_X" or "part_XX") (optional)
12386   if (strPrefix(s_ptr, pattern_2))
12387   {
12388     s_ptr += strlen(pattern_2);
12389
12390     if (*s_ptr >= '0' && *s_ptr <= '9')
12391     {
12392       int gic_part_nr = (*s_ptr++ - '0');
12393
12394       if (*s_ptr >= '0' && *s_ptr <= '9')
12395         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12396
12397       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12398         return ANIM_EVENT_NONE;
12399
12400       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12401     }
12402     else
12403     {
12404       // invalid animation part number specified
12405
12406       return ANIM_EVENT_NONE;
12407     }
12408   }
12409
12410   // discard result if next character is neither delimiter nor whitespace
12411   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12412         *s_ptr == ' ' || *s_ptr == '\t'))
12413     return ANIM_EVENT_NONE;
12414
12415   return result;
12416 }
12417
12418 static int get_anim_parameter_values(char *s)
12419 {
12420   int list_pos = ANIM_EVENT_UNDEFINED;
12421   int event_value = ANIM_EVENT_DEFAULT;
12422
12423   if (string_has_parameter(s, "any"))
12424     event_value |= ANIM_EVENT_ANY;
12425
12426   if (string_has_parameter(s, "click:self") ||
12427       string_has_parameter(s, "click") ||
12428       string_has_parameter(s, "self"))
12429     event_value |= ANIM_EVENT_SELF;
12430
12431   if (string_has_parameter(s, "unclick:any"))
12432     event_value |= ANIM_EVENT_UNCLICK_ANY;
12433
12434   // if animation event found, add it to global animation event list
12435   if (event_value != ANIM_EVENT_NONE)
12436     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12437
12438   while (s != NULL)
12439   {
12440     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12441     event_value = get_anim_parameter_value(s);
12442
12443     // if animation event found, add it to global animation event list
12444     if (event_value != ANIM_EVENT_NONE)
12445       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12446
12447     // continue with next part of the string, starting with next comma
12448     s = strchr(s + 1, ',');
12449   }
12450
12451   return list_pos;
12452 }
12453
12454 static int get_anim_action_parameter_value(char *token)
12455 {
12456   // check most common default case first to massively speed things up
12457   if (strEqual(token, ARG_UNDEFINED))
12458     return ANIM_EVENT_ACTION_NONE;
12459
12460   int result = getImageIDFromToken(token);
12461
12462   if (result == -1)
12463   {
12464     char *gfx_token = getStringCat2("gfx.", token);
12465
12466     result = getImageIDFromToken(gfx_token);
12467
12468     checked_free(gfx_token);
12469   }
12470
12471   if (result == -1)
12472   {
12473     Key key = getKeyFromX11KeyName(token);
12474
12475     if (key != KSYM_UNDEFINED)
12476       result = -(int)key;
12477   }
12478
12479   if (result == -1)
12480   {
12481     if (isURL(token))
12482     {
12483       result = get_hash_from_string(token);     // unsigned int => int
12484       result = ABS(result);                     // may be negative now
12485       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12486
12487       setHashEntry(anim_url_hash, int2str(result, 0), token);
12488     }
12489   }
12490
12491   if (result == -1)
12492     result = ANIM_EVENT_ACTION_NONE;
12493
12494   return result;
12495 }
12496
12497 int get_parameter_value(char *value_raw, char *suffix, int type)
12498 {
12499   char *value = getStringToLower(value_raw);
12500   int result = 0;       // probably a save default value
12501
12502   if (strEqual(suffix, ".direction"))
12503   {
12504     result = (strEqual(value, "left")  ? MV_LEFT :
12505               strEqual(value, "right") ? MV_RIGHT :
12506               strEqual(value, "up")    ? MV_UP :
12507               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12508   }
12509   else if (strEqual(suffix, ".position"))
12510   {
12511     result = (strEqual(value, "left")   ? POS_LEFT :
12512               strEqual(value, "right")  ? POS_RIGHT :
12513               strEqual(value, "top")    ? POS_TOP :
12514               strEqual(value, "upper")  ? POS_UPPER :
12515               strEqual(value, "middle") ? POS_MIDDLE :
12516               strEqual(value, "lower")  ? POS_LOWER :
12517               strEqual(value, "bottom") ? POS_BOTTOM :
12518               strEqual(value, "any")    ? POS_ANY :
12519               strEqual(value, "ce")     ? POS_CE :
12520               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12521               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12522   }
12523   else if (strEqual(suffix, ".align"))
12524   {
12525     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12526               strEqual(value, "right")  ? ALIGN_RIGHT :
12527               strEqual(value, "center") ? ALIGN_CENTER :
12528               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12529   }
12530   else if (strEqual(suffix, ".valign"))
12531   {
12532     result = (strEqual(value, "top")    ? VALIGN_TOP :
12533               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12534               strEqual(value, "middle") ? VALIGN_MIDDLE :
12535               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12536   }
12537   else if (strEqual(suffix, ".anim_mode"))
12538   {
12539     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12540               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12541               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12542               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12543               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12544               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12545               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12546               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12547               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12548               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12549               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12550               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12551               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12552               string_has_parameter(value, "all")        ? ANIM_ALL :
12553               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12554               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12555               ANIM_DEFAULT);
12556
12557     if (string_has_parameter(value, "once"))
12558       result |= ANIM_ONCE;
12559
12560     if (string_has_parameter(value, "reverse"))
12561       result |= ANIM_REVERSE;
12562
12563     if (string_has_parameter(value, "opaque_player"))
12564       result |= ANIM_OPAQUE_PLAYER;
12565
12566     if (string_has_parameter(value, "static_panel"))
12567       result |= ANIM_STATIC_PANEL;
12568   }
12569   else if (strEqual(suffix, ".init_event") ||
12570            strEqual(suffix, ".anim_event"))
12571   {
12572     result = get_anim_parameter_values(value);
12573   }
12574   else if (strEqual(suffix, ".init_delay_action") ||
12575            strEqual(suffix, ".anim_delay_action") ||
12576            strEqual(suffix, ".post_delay_action") ||
12577            strEqual(suffix, ".init_event_action") ||
12578            strEqual(suffix, ".anim_event_action"))
12579   {
12580     result = get_anim_action_parameter_value(value_raw);
12581   }
12582   else if (strEqual(suffix, ".class"))
12583   {
12584     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12585               get_hash_from_string(value));
12586   }
12587   else if (strEqual(suffix, ".style"))
12588   {
12589     result = STYLE_DEFAULT;
12590
12591     if (string_has_parameter(value, "accurate_borders"))
12592       result |= STYLE_ACCURATE_BORDERS;
12593
12594     if (string_has_parameter(value, "inner_corners"))
12595       result |= STYLE_INNER_CORNERS;
12596
12597     if (string_has_parameter(value, "reverse"))
12598       result |= STYLE_REVERSE;
12599
12600     if (string_has_parameter(value, "leftmost_position"))
12601       result |= STYLE_LEFTMOST_POSITION;
12602
12603     if (string_has_parameter(value, "block_clicks"))
12604       result |= STYLE_BLOCK;
12605
12606     if (string_has_parameter(value, "passthrough_clicks"))
12607       result |= STYLE_PASSTHROUGH;
12608
12609     if (string_has_parameter(value, "multiple_actions"))
12610       result |= STYLE_MULTIPLE_ACTIONS;
12611
12612     if (string_has_parameter(value, "consume_ce_event"))
12613       result |= STYLE_CONSUME_CE_EVENT;
12614   }
12615   else if (strEqual(suffix, ".fade_mode"))
12616   {
12617     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12618               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12619               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12620               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12621               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12622               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12623               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12624               FADE_MODE_DEFAULT);
12625   }
12626   else if (strEqual(suffix, ".auto_delay_unit"))
12627   {
12628     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12629               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12630               AUTO_DELAY_UNIT_DEFAULT);
12631   }
12632   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12633   {
12634     result = gfx.get_font_from_token_function(value);
12635   }
12636   else          // generic parameter of type integer or boolean
12637   {
12638     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12639               type == TYPE_INTEGER ? get_integer_from_string(value) :
12640               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12641               ARG_UNDEFINED_VALUE);
12642   }
12643
12644   free(value);
12645
12646   return result;
12647 }
12648
12649 static int get_token_parameter_value(char *token, char *value_raw)
12650 {
12651   char *suffix;
12652
12653   if (token == NULL || value_raw == NULL)
12654     return ARG_UNDEFINED_VALUE;
12655
12656   suffix = strrchr(token, '.');
12657   if (suffix == NULL)
12658     suffix = token;
12659
12660   if (strEqual(suffix, ".element"))
12661     return getElementFromToken(value_raw);
12662
12663   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12664   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12665 }
12666
12667 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12668                                      boolean ignore_defaults)
12669 {
12670   int i;
12671
12672   for (i = 0; image_config_vars[i].token != NULL; i++)
12673   {
12674     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12675
12676     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12677     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12678       continue;
12679
12680     if (value != NULL)
12681       *image_config_vars[i].value =
12682         get_token_parameter_value(image_config_vars[i].token, value);
12683   }
12684 }
12685
12686 void InitMenuDesignSettings_Static(void)
12687 {
12688   // always start with reliable default values from static default config
12689   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12690 }
12691
12692 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12693 {
12694   int i;
12695
12696   // the following initializes hierarchical values from static configuration
12697
12698   // special case: initialize "ARG_DEFAULT" values in static default config
12699   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12700   titlescreen_initial_first_default.fade_mode  =
12701     title_initial_first_default.fade_mode;
12702   titlescreen_initial_first_default.fade_delay =
12703     title_initial_first_default.fade_delay;
12704   titlescreen_initial_first_default.post_delay =
12705     title_initial_first_default.post_delay;
12706   titlescreen_initial_first_default.auto_delay =
12707     title_initial_first_default.auto_delay;
12708   titlescreen_initial_first_default.auto_delay_unit =
12709     title_initial_first_default.auto_delay_unit;
12710   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12711   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12712   titlescreen_first_default.post_delay = title_first_default.post_delay;
12713   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12714   titlescreen_first_default.auto_delay_unit =
12715     title_first_default.auto_delay_unit;
12716   titlemessage_initial_first_default.fade_mode  =
12717     title_initial_first_default.fade_mode;
12718   titlemessage_initial_first_default.fade_delay =
12719     title_initial_first_default.fade_delay;
12720   titlemessage_initial_first_default.post_delay =
12721     title_initial_first_default.post_delay;
12722   titlemessage_initial_first_default.auto_delay =
12723     title_initial_first_default.auto_delay;
12724   titlemessage_initial_first_default.auto_delay_unit =
12725     title_initial_first_default.auto_delay_unit;
12726   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12727   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12728   titlemessage_first_default.post_delay = title_first_default.post_delay;
12729   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12730   titlemessage_first_default.auto_delay_unit =
12731     title_first_default.auto_delay_unit;
12732
12733   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12734   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12735   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12736   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12737   titlescreen_initial_default.auto_delay_unit =
12738     title_initial_default.auto_delay_unit;
12739   titlescreen_default.fade_mode  = title_default.fade_mode;
12740   titlescreen_default.fade_delay = title_default.fade_delay;
12741   titlescreen_default.post_delay = title_default.post_delay;
12742   titlescreen_default.auto_delay = title_default.auto_delay;
12743   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12744   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12745   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12746   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12747   titlemessage_initial_default.auto_delay_unit =
12748     title_initial_default.auto_delay_unit;
12749   titlemessage_default.fade_mode  = title_default.fade_mode;
12750   titlemessage_default.fade_delay = title_default.fade_delay;
12751   titlemessage_default.post_delay = title_default.post_delay;
12752   titlemessage_default.auto_delay = title_default.auto_delay;
12753   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12754
12755   // special case: initialize "ARG_DEFAULT" values in static default config
12756   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12757   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12758   {
12759     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12760     titlescreen_first[i] = titlescreen_first_default;
12761     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12762     titlemessage_first[i] = titlemessage_first_default;
12763
12764     titlescreen_initial[i] = titlescreen_initial_default;
12765     titlescreen[i] = titlescreen_default;
12766     titlemessage_initial[i] = titlemessage_initial_default;
12767     titlemessage[i] = titlemessage_default;
12768   }
12769
12770   // special case: initialize "ARG_DEFAULT" values in static default config
12771   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12772   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12773   {
12774     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12775       continue;
12776
12777     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12778     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12779     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12780   }
12781
12782   // special case: initialize "ARG_DEFAULT" values in static default config
12783   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12784   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12785   {
12786     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12787     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12788     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12789
12790     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12791       continue;
12792
12793     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12794   }
12795 }
12796
12797 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12798 {
12799   static struct
12800   {
12801     struct XY *dst, *src;
12802   }
12803   game_buttons_xy[] =
12804   {
12805     { &game.button.save,        &game.button.stop       },
12806     { &game.button.pause2,      &game.button.pause      },
12807     { &game.button.load,        &game.button.play       },
12808     { &game.button.undo,        &game.button.stop       },
12809     { &game.button.redo,        &game.button.play       },
12810
12811     { NULL,                     NULL                    }
12812   };
12813   int i, j;
12814
12815   // special case: initialize later added SETUP list size from LEVELS value
12816   if (menu.list_size[GAME_MODE_SETUP] == -1)
12817     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12818
12819   // set default position for snapshot buttons to stop/pause/play buttons
12820   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12821     if ((*game_buttons_xy[i].dst).x == -1 &&
12822         (*game_buttons_xy[i].dst).y == -1)
12823       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12824
12825   // --------------------------------------------------------------------------
12826   // dynamic viewports (including playfield margins, borders and alignments)
12827   // --------------------------------------------------------------------------
12828
12829   // dynamic viewports currently only supported for landscape mode
12830   int display_width  = MAX(video.display_width, video.display_height);
12831   int display_height = MIN(video.display_width, video.display_height);
12832
12833   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12834   {
12835     struct RectWithBorder *vp_window    = &viewport.window[i];
12836     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12837     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12838     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12839     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12840     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12841     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12842     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12843
12844     // adjust window size if min/max width/height is specified
12845
12846     if (vp_window->min_width != -1)
12847     {
12848       int window_width = display_width;
12849
12850       // when using static window height, use aspect ratio of display
12851       if (vp_window->min_height == -1)
12852         window_width = vp_window->height * display_width / display_height;
12853
12854       vp_window->width = MAX(vp_window->min_width, window_width);
12855     }
12856
12857     if (vp_window->min_height != -1)
12858     {
12859       int window_height = display_height;
12860
12861       // when using static window width, use aspect ratio of display
12862       if (vp_window->min_width == -1)
12863         window_height = vp_window->width * display_height / display_width;
12864
12865       vp_window->height = MAX(vp_window->min_height, window_height);
12866     }
12867
12868     if (vp_window->max_width != -1)
12869       vp_window->width = MIN(vp_window->width, vp_window->max_width);
12870
12871     if (vp_window->max_height != -1)
12872       vp_window->height = MIN(vp_window->height, vp_window->max_height);
12873
12874     int playfield_width  = vp_window->width;
12875     int playfield_height = vp_window->height;
12876
12877     // adjust playfield size and position according to specified margins
12878
12879     playfield_width  -= vp_playfield->margin_left;
12880     playfield_width  -= vp_playfield->margin_right;
12881
12882     playfield_height -= vp_playfield->margin_top;
12883     playfield_height -= vp_playfield->margin_bottom;
12884
12885     // adjust playfield size if min/max width/height is specified
12886
12887     if (vp_playfield->min_width != -1)
12888       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12889
12890     if (vp_playfield->min_height != -1)
12891       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12892
12893     if (vp_playfield->max_width != -1)
12894       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12895
12896     if (vp_playfield->max_height != -1)
12897       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12898
12899     // adjust playfield position according to specified alignment
12900
12901     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12902       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12903     else if (vp_playfield->align == ALIGN_CENTER)
12904       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12905     else if (vp_playfield->align == ALIGN_RIGHT)
12906       vp_playfield->x += playfield_width - vp_playfield->width;
12907
12908     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12909       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12910     else if (vp_playfield->valign == VALIGN_MIDDLE)
12911       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12912     else if (vp_playfield->valign == VALIGN_BOTTOM)
12913       vp_playfield->y += playfield_height - vp_playfield->height;
12914
12915     vp_playfield->x += vp_playfield->margin_left;
12916     vp_playfield->y += vp_playfield->margin_top;
12917
12918     // adjust individual playfield borders if only default border is specified
12919
12920     if (vp_playfield->border_left == -1)
12921       vp_playfield->border_left = vp_playfield->border_size;
12922     if (vp_playfield->border_right == -1)
12923       vp_playfield->border_right = vp_playfield->border_size;
12924     if (vp_playfield->border_top == -1)
12925       vp_playfield->border_top = vp_playfield->border_size;
12926     if (vp_playfield->border_bottom == -1)
12927       vp_playfield->border_bottom = vp_playfield->border_size;
12928
12929     // set dynamic playfield borders if borders are specified as undefined
12930     // (but only if window size was dynamic and playfield size was static)
12931
12932     if (dynamic_window_width && !dynamic_playfield_width)
12933     {
12934       if (vp_playfield->border_left == -1)
12935       {
12936         vp_playfield->border_left = (vp_playfield->x -
12937                                      vp_playfield->margin_left);
12938         vp_playfield->x     -= vp_playfield->border_left;
12939         vp_playfield->width += vp_playfield->border_left;
12940       }
12941
12942       if (vp_playfield->border_right == -1)
12943       {
12944         vp_playfield->border_right = (vp_window->width -
12945                                       vp_playfield->x -
12946                                       vp_playfield->width -
12947                                       vp_playfield->margin_right);
12948         vp_playfield->width += vp_playfield->border_right;
12949       }
12950     }
12951
12952     if (dynamic_window_height && !dynamic_playfield_height)
12953     {
12954       if (vp_playfield->border_top == -1)
12955       {
12956         vp_playfield->border_top = (vp_playfield->y -
12957                                     vp_playfield->margin_top);
12958         vp_playfield->y      -= vp_playfield->border_top;
12959         vp_playfield->height += vp_playfield->border_top;
12960       }
12961
12962       if (vp_playfield->border_bottom == -1)
12963       {
12964         vp_playfield->border_bottom = (vp_window->height -
12965                                        vp_playfield->y -
12966                                        vp_playfield->height -
12967                                        vp_playfield->margin_bottom);
12968         vp_playfield->height += vp_playfield->border_bottom;
12969       }
12970     }
12971
12972     // adjust playfield size to be a multiple of a defined alignment tile size
12973
12974     int align_size = vp_playfield->align_size;
12975     int playfield_xtiles = vp_playfield->width  / align_size;
12976     int playfield_ytiles = vp_playfield->height / align_size;
12977     int playfield_width_corrected  = playfield_xtiles * align_size;
12978     int playfield_height_corrected = playfield_ytiles * align_size;
12979     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12980                                  i == GFX_SPECIAL_ARG_EDITOR);
12981
12982     if (is_playfield_mode &&
12983         dynamic_playfield_width &&
12984         vp_playfield->width != playfield_width_corrected)
12985     {
12986       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12987
12988       vp_playfield->width = playfield_width_corrected;
12989
12990       if (vp_playfield->align == ALIGN_LEFT)
12991       {
12992         vp_playfield->border_left += playfield_xdiff;
12993       }
12994       else if (vp_playfield->align == ALIGN_RIGHT)
12995       {
12996         vp_playfield->border_right += playfield_xdiff;
12997       }
12998       else if (vp_playfield->align == ALIGN_CENTER)
12999       {
13000         int border_left_diff  = playfield_xdiff / 2;
13001         int border_right_diff = playfield_xdiff - border_left_diff;
13002
13003         vp_playfield->border_left  += border_left_diff;
13004         vp_playfield->border_right += border_right_diff;
13005       }
13006     }
13007
13008     if (is_playfield_mode &&
13009         dynamic_playfield_height &&
13010         vp_playfield->height != playfield_height_corrected)
13011     {
13012       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13013
13014       vp_playfield->height = playfield_height_corrected;
13015
13016       if (vp_playfield->valign == VALIGN_TOP)
13017       {
13018         vp_playfield->border_top += playfield_ydiff;
13019       }
13020       else if (vp_playfield->align == VALIGN_BOTTOM)
13021       {
13022         vp_playfield->border_right += playfield_ydiff;
13023       }
13024       else if (vp_playfield->align == VALIGN_MIDDLE)
13025       {
13026         int border_top_diff    = playfield_ydiff / 2;
13027         int border_bottom_diff = playfield_ydiff - border_top_diff;
13028
13029         vp_playfield->border_top    += border_top_diff;
13030         vp_playfield->border_bottom += border_bottom_diff;
13031       }
13032     }
13033
13034     // adjust door positions according to specified alignment
13035
13036     for (j = 0; j < 2; j++)
13037     {
13038       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13039
13040       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13041         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13042       else if (vp_door->align == ALIGN_CENTER)
13043         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13044       else if (vp_door->align == ALIGN_RIGHT)
13045         vp_door->x += vp_window->width - vp_door->width;
13046
13047       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13048         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13049       else if (vp_door->valign == VALIGN_MIDDLE)
13050         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13051       else if (vp_door->valign == VALIGN_BOTTOM)
13052         vp_door->y += vp_window->height - vp_door->height;
13053     }
13054   }
13055 }
13056
13057 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13058 {
13059   static struct
13060   {
13061     struct XYTileSize *dst, *src;
13062     int graphic;
13063   }
13064   editor_buttons_xy[] =
13065   {
13066     {
13067       &editor.button.element_left,      &editor.palette.element_left,
13068       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13069     },
13070     {
13071       &editor.button.element_middle,    &editor.palette.element_middle,
13072       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13073     },
13074     {
13075       &editor.button.element_right,     &editor.palette.element_right,
13076       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13077     },
13078
13079     { NULL,                     NULL                    }
13080   };
13081   int i;
13082
13083   // set default position for element buttons to element graphics
13084   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13085   {
13086     if ((*editor_buttons_xy[i].dst).x == -1 &&
13087         (*editor_buttons_xy[i].dst).y == -1)
13088     {
13089       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13090
13091       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13092
13093       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13094     }
13095   }
13096
13097   // adjust editor palette rows and columns if specified to be dynamic
13098
13099   if (editor.palette.cols == -1)
13100   {
13101     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13102     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13103     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13104
13105     editor.palette.cols = (vp_width - sc_width) / bt_width;
13106
13107     if (editor.palette.x == -1)
13108     {
13109       int palette_width = editor.palette.cols * bt_width + sc_width;
13110
13111       editor.palette.x = (vp_width - palette_width) / 2;
13112     }
13113   }
13114
13115   if (editor.palette.rows == -1)
13116   {
13117     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13118     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13119     int tx_height = getFontHeight(FONT_TEXT_2);
13120
13121     editor.palette.rows = (vp_height - tx_height) / bt_height;
13122
13123     if (editor.palette.y == -1)
13124     {
13125       int palette_height = editor.palette.rows * bt_height + tx_height;
13126
13127       editor.palette.y = (vp_height - palette_height) / 2;
13128     }
13129   }
13130 }
13131
13132 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13133                                                       boolean initialize)
13134 {
13135   // special case: check if network and preview player positions are redefined,
13136   // to compare this later against the main menu level preview being redefined
13137   struct TokenIntPtrInfo menu_config_players[] =
13138   {
13139     { "main.network_players.x", &menu.main.network_players.redefined    },
13140     { "main.network_players.y", &menu.main.network_players.redefined    },
13141     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13142     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13143     { "preview.x",              &preview.redefined                      },
13144     { "preview.y",              &preview.redefined                      }
13145   };
13146   int i;
13147
13148   if (initialize)
13149   {
13150     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13151       *menu_config_players[i].value = FALSE;
13152   }
13153   else
13154   {
13155     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13156       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13157         *menu_config_players[i].value = TRUE;
13158   }
13159 }
13160
13161 static void InitMenuDesignSettings_PreviewPlayers(void)
13162 {
13163   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13164 }
13165
13166 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13167 {
13168   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13169 }
13170
13171 static void LoadMenuDesignSettingsFromFilename(char *filename)
13172 {
13173   static struct TitleFadingInfo tfi;
13174   static struct TitleMessageInfo tmi;
13175   static struct TokenInfo title_tokens[] =
13176   {
13177     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13178     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13179     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13180     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13181     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13182
13183     { -1,               NULL,                   NULL                    }
13184   };
13185   static struct TokenInfo titlemessage_tokens[] =
13186   {
13187     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13188     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13189     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13190     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13191     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13192     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13193     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13194     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13195     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13196     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13197     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13198     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13199     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13200     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13201     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13202     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13203     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13204     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13205
13206     { -1,               NULL,                   NULL                    }
13207   };
13208   static struct
13209   {
13210     struct TitleFadingInfo *info;
13211     char *text;
13212   }
13213   title_info[] =
13214   {
13215     // initialize first titles from "enter screen" definitions, if defined
13216     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13217     { &title_first_default,             "menu.enter_screen.TITLE"       },
13218
13219     // initialize title screens from "next screen" definitions, if defined
13220     { &title_initial_default,           "menu.next_screen.TITLE"        },
13221     { &title_default,                   "menu.next_screen.TITLE"        },
13222
13223     { NULL,                             NULL                            }
13224   };
13225   static struct
13226   {
13227     struct TitleMessageInfo *array;
13228     char *text;
13229   }
13230   titlemessage_arrays[] =
13231   {
13232     // initialize first titles from "enter screen" definitions, if defined
13233     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13234     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13235     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13236     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13237
13238     // initialize titles from "next screen" definitions, if defined
13239     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13240     { titlescreen,                      "menu.next_screen.TITLE"        },
13241     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13242     { titlemessage,                     "menu.next_screen.TITLE"        },
13243
13244     // overwrite titles with title definitions, if defined
13245     { titlescreen_initial_first,        "[title_initial]"               },
13246     { titlescreen_first,                "[title]"                       },
13247     { titlemessage_initial_first,       "[title_initial]"               },
13248     { titlemessage_first,               "[title]"                       },
13249
13250     { titlescreen_initial,              "[title_initial]"               },
13251     { titlescreen,                      "[title]"                       },
13252     { titlemessage_initial,             "[title_initial]"               },
13253     { titlemessage,                     "[title]"                       },
13254
13255     // overwrite titles with title screen/message definitions, if defined
13256     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13257     { titlescreen_first,                "[titlescreen]"                 },
13258     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13259     { titlemessage_first,               "[titlemessage]"                },
13260
13261     { titlescreen_initial,              "[titlescreen_initial]"         },
13262     { titlescreen,                      "[titlescreen]"                 },
13263     { titlemessage_initial,             "[titlemessage_initial]"        },
13264     { titlemessage,                     "[titlemessage]"                },
13265
13266     { NULL,                             NULL                            }
13267   };
13268   SetupFileHash *setup_file_hash;
13269   int i, j, k;
13270
13271   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13272     return;
13273
13274   // the following initializes hierarchical values from dynamic configuration
13275
13276   // special case: initialize with default values that may be overwritten
13277   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13278   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13279   {
13280     struct TokenIntPtrInfo menu_config[] =
13281     {
13282       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13283       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13284       { "menu.list_size",       &menu.list_size[i]      }
13285     };
13286
13287     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13288     {
13289       char *token = menu_config[j].token;
13290       char *value = getHashEntry(setup_file_hash, token);
13291
13292       if (value != NULL)
13293         *menu_config[j].value = get_integer_from_string(value);
13294     }
13295   }
13296
13297   // special case: initialize with default values that may be overwritten
13298   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13299   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13300   {
13301     struct TokenIntPtrInfo menu_config[] =
13302     {
13303       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13304       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13305       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13306       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13307       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13308     };
13309
13310     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13311     {
13312       char *token = menu_config[j].token;
13313       char *value = getHashEntry(setup_file_hash, token);
13314
13315       if (value != NULL)
13316         *menu_config[j].value = get_integer_from_string(value);
13317     }
13318   }
13319
13320   // special case: initialize with default values that may be overwritten
13321   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13322   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13323   {
13324     struct TokenIntPtrInfo menu_config[] =
13325     {
13326       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13327       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13328     };
13329
13330     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13331     {
13332       char *token = menu_config[j].token;
13333       char *value = getHashEntry(setup_file_hash, token);
13334
13335       if (value != NULL)
13336         *menu_config[j].value = get_integer_from_string(value);
13337     }
13338   }
13339
13340   // special case: initialize with default values that may be overwritten
13341   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13342   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13343   {
13344     struct TokenIntPtrInfo menu_config[] =
13345     {
13346       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13347       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13348       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13349       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13350       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13351       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13352       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13353       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13354       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13355       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13356     };
13357
13358     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13359     {
13360       char *token = menu_config[j].token;
13361       char *value = getHashEntry(setup_file_hash, token);
13362
13363       if (value != NULL)
13364         *menu_config[j].value = get_integer_from_string(value);
13365     }
13366   }
13367
13368   // special case: initialize with default values that may be overwritten
13369   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13370   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13371   {
13372     struct TokenIntPtrInfo menu_config[] =
13373     {
13374       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13375       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13376       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13377       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13378       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13379       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13380       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13381       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13382       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13383     };
13384
13385     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13386     {
13387       char *token = menu_config[j].token;
13388       char *value = getHashEntry(setup_file_hash, token);
13389
13390       if (value != NULL)
13391         *menu_config[j].value = get_token_parameter_value(token, value);
13392     }
13393   }
13394
13395   // special case: initialize with default values that may be overwritten
13396   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13397   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13398   {
13399     struct
13400     {
13401       char *token_prefix;
13402       struct RectWithBorder *struct_ptr;
13403     }
13404     vp_struct[] =
13405     {
13406       { "viewport.window",      &viewport.window[i]     },
13407       { "viewport.playfield",   &viewport.playfield[i]  },
13408       { "viewport.door_1",      &viewport.door_1[i]     },
13409       { "viewport.door_2",      &viewport.door_2[i]     }
13410     };
13411
13412     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13413     {
13414       struct TokenIntPtrInfo vp_config[] =
13415       {
13416         { ".x",                 &vp_struct[j].struct_ptr->x             },
13417         { ".y",                 &vp_struct[j].struct_ptr->y             },
13418         { ".width",             &vp_struct[j].struct_ptr->width         },
13419         { ".height",            &vp_struct[j].struct_ptr->height        },
13420         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13421         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13422         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13423         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13424         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13425         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13426         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13427         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13428         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13429         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13430         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13431         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13432         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13433         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13434         { ".align",             &vp_struct[j].struct_ptr->align         },
13435         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13436       };
13437
13438       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13439       {
13440         char *token = getStringCat2(vp_struct[j].token_prefix,
13441                                     vp_config[k].token);
13442         char *value = getHashEntry(setup_file_hash, token);
13443
13444         if (value != NULL)
13445           *vp_config[k].value = get_token_parameter_value(token, value);
13446
13447         free(token);
13448       }
13449     }
13450   }
13451
13452   // special case: initialize with default values that may be overwritten
13453   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13454   for (i = 0; title_info[i].info != NULL; i++)
13455   {
13456     struct TitleFadingInfo *info = title_info[i].info;
13457     char *base_token = title_info[i].text;
13458
13459     for (j = 0; title_tokens[j].type != -1; j++)
13460     {
13461       char *token = getStringCat2(base_token, title_tokens[j].text);
13462       char *value = getHashEntry(setup_file_hash, token);
13463
13464       if (value != NULL)
13465       {
13466         int parameter_value = get_token_parameter_value(token, value);
13467
13468         tfi = *info;
13469
13470         *(int *)title_tokens[j].value = (int)parameter_value;
13471
13472         *info = tfi;
13473       }
13474
13475       free(token);
13476     }
13477   }
13478
13479   // special case: initialize with default values that may be overwritten
13480   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13481   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13482   {
13483     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13484     char *base_token = titlemessage_arrays[i].text;
13485
13486     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13487     {
13488       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13489       char *value = getHashEntry(setup_file_hash, token);
13490
13491       if (value != NULL)
13492       {
13493         int parameter_value = get_token_parameter_value(token, value);
13494
13495         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13496         {
13497           tmi = array[k];
13498
13499           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13500             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13501           else
13502             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13503
13504           array[k] = tmi;
13505         }
13506       }
13507
13508       free(token);
13509     }
13510   }
13511
13512   // read (and overwrite with) values that may be specified in config file
13513   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13514
13515   // special case: check if network and preview player positions are redefined
13516   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13517
13518   freeSetupFileHash(setup_file_hash);
13519 }
13520
13521 void LoadMenuDesignSettings(void)
13522 {
13523   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13524
13525   InitMenuDesignSettings_Static();
13526   InitMenuDesignSettings_SpecialPreProcessing();
13527   InitMenuDesignSettings_PreviewPlayers();
13528
13529   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13530   {
13531     // first look for special settings configured in level series config
13532     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13533
13534     if (fileExists(filename_base))
13535       LoadMenuDesignSettingsFromFilename(filename_base);
13536   }
13537
13538   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13539
13540   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13541     LoadMenuDesignSettingsFromFilename(filename_local);
13542
13543   InitMenuDesignSettings_SpecialPostProcessing();
13544 }
13545
13546 void LoadMenuDesignSettings_AfterGraphics(void)
13547 {
13548   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13549 }
13550
13551 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13552                                 boolean ignore_defaults)
13553 {
13554   int i;
13555
13556   for (i = 0; sound_config_vars[i].token != NULL; i++)
13557   {
13558     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13559
13560     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13561     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13562       continue;
13563
13564     if (value != NULL)
13565       *sound_config_vars[i].value =
13566         get_token_parameter_value(sound_config_vars[i].token, value);
13567   }
13568 }
13569
13570 void InitSoundSettings_Static(void)
13571 {
13572   // always start with reliable default values from static default config
13573   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13574 }
13575
13576 static void LoadSoundSettingsFromFilename(char *filename)
13577 {
13578   SetupFileHash *setup_file_hash;
13579
13580   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13581     return;
13582
13583   // read (and overwrite with) values that may be specified in config file
13584   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13585
13586   freeSetupFileHash(setup_file_hash);
13587 }
13588
13589 void LoadSoundSettings(void)
13590 {
13591   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13592
13593   InitSoundSettings_Static();
13594
13595   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13596   {
13597     // first look for special settings configured in level series config
13598     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13599
13600     if (fileExists(filename_base))
13601       LoadSoundSettingsFromFilename(filename_base);
13602   }
13603
13604   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13605
13606   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13607     LoadSoundSettingsFromFilename(filename_local);
13608 }
13609
13610 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13611 {
13612   char *filename = getEditorSetupFilename();
13613   SetupFileList *setup_file_list, *list;
13614   SetupFileHash *element_hash;
13615   int num_unknown_tokens = 0;
13616   int i;
13617
13618   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13619     return;
13620
13621   element_hash = newSetupFileHash();
13622
13623   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13624     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13625
13626   // determined size may be larger than needed (due to unknown elements)
13627   *num_elements = 0;
13628   for (list = setup_file_list; list != NULL; list = list->next)
13629     (*num_elements)++;
13630
13631   // add space for up to 3 more elements for padding that may be needed
13632   *num_elements += 3;
13633
13634   // free memory for old list of elements, if needed
13635   checked_free(*elements);
13636
13637   // allocate memory for new list of elements
13638   *elements = checked_malloc(*num_elements * sizeof(int));
13639
13640   *num_elements = 0;
13641   for (list = setup_file_list; list != NULL; list = list->next)
13642   {
13643     char *value = getHashEntry(element_hash, list->token);
13644
13645     if (value == NULL)          // try to find obsolete token mapping
13646     {
13647       char *mapped_token = get_mapped_token(list->token);
13648
13649       if (mapped_token != NULL)
13650       {
13651         value = getHashEntry(element_hash, mapped_token);
13652
13653         free(mapped_token);
13654       }
13655     }
13656
13657     if (value != NULL)
13658     {
13659       (*elements)[(*num_elements)++] = atoi(value);
13660     }
13661     else
13662     {
13663       if (num_unknown_tokens == 0)
13664       {
13665         Warn("---");
13666         Warn("unknown token(s) found in config file:");
13667         Warn("- config file: '%s'", filename);
13668
13669         num_unknown_tokens++;
13670       }
13671
13672       Warn("- token: '%s'", list->token);
13673     }
13674   }
13675
13676   if (num_unknown_tokens > 0)
13677     Warn("---");
13678
13679   while (*num_elements % 4)     // pad with empty elements, if needed
13680     (*elements)[(*num_elements)++] = EL_EMPTY;
13681
13682   freeSetupFileList(setup_file_list);
13683   freeSetupFileHash(element_hash);
13684
13685 #if 0
13686   for (i = 0; i < *num_elements; i++)
13687     Debug("editor", "element '%s' [%d]\n",
13688           element_info[(*elements)[i]].token_name, (*elements)[i]);
13689 #endif
13690 }
13691
13692 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13693                                                      boolean is_sound)
13694 {
13695   SetupFileHash *setup_file_hash = NULL;
13696   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13697   char *filename_music, *filename_prefix, *filename_info;
13698   struct
13699   {
13700     char *token;
13701     char **value_ptr;
13702   }
13703   token_to_value_ptr[] =
13704   {
13705     { "title_header",   &tmp_music_file_info.title_header       },
13706     { "artist_header",  &tmp_music_file_info.artist_header      },
13707     { "album_header",   &tmp_music_file_info.album_header       },
13708     { "year_header",    &tmp_music_file_info.year_header        },
13709     { "played_header",  &tmp_music_file_info.played_header      },
13710
13711     { "title",          &tmp_music_file_info.title              },
13712     { "artist",         &tmp_music_file_info.artist             },
13713     { "album",          &tmp_music_file_info.album              },
13714     { "year",           &tmp_music_file_info.year               },
13715     { "played",         &tmp_music_file_info.played             },
13716
13717     { NULL,             NULL                                    },
13718   };
13719   int i;
13720
13721   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13722                     getCustomMusicFilename(basename));
13723
13724   if (filename_music == NULL)
13725     return NULL;
13726
13727   // ---------- try to replace file extension ----------
13728
13729   filename_prefix = getStringCopy(filename_music);
13730   if (strrchr(filename_prefix, '.') != NULL)
13731     *strrchr(filename_prefix, '.') = '\0';
13732   filename_info = getStringCat2(filename_prefix, ".txt");
13733
13734   if (fileExists(filename_info))
13735     setup_file_hash = loadSetupFileHash(filename_info);
13736
13737   free(filename_prefix);
13738   free(filename_info);
13739
13740   if (setup_file_hash == NULL)
13741   {
13742     // ---------- try to add file extension ----------
13743
13744     filename_prefix = getStringCopy(filename_music);
13745     filename_info = getStringCat2(filename_prefix, ".txt");
13746
13747     if (fileExists(filename_info))
13748       setup_file_hash = loadSetupFileHash(filename_info);
13749
13750     free(filename_prefix);
13751     free(filename_info);
13752   }
13753
13754   if (setup_file_hash == NULL)
13755     return NULL;
13756
13757   // ---------- music file info found ----------
13758
13759   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13760
13761   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13762   {
13763     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13764
13765     *token_to_value_ptr[i].value_ptr =
13766       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13767   }
13768
13769   tmp_music_file_info.basename = getStringCopy(basename);
13770   tmp_music_file_info.music = music;
13771   tmp_music_file_info.is_sound = is_sound;
13772
13773   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13774   *new_music_file_info = tmp_music_file_info;
13775
13776   return new_music_file_info;
13777 }
13778
13779 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13780 {
13781   return get_music_file_info_ext(basename, music, FALSE);
13782 }
13783
13784 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13785 {
13786   return get_music_file_info_ext(basename, sound, TRUE);
13787 }
13788
13789 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13790                                      char *basename, boolean is_sound)
13791 {
13792   for (; list != NULL; list = list->next)
13793     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13794       return TRUE;
13795
13796   return FALSE;
13797 }
13798
13799 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13800 {
13801   return music_info_listed_ext(list, basename, FALSE);
13802 }
13803
13804 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13805 {
13806   return music_info_listed_ext(list, basename, TRUE);
13807 }
13808
13809 void LoadMusicInfo(void)
13810 {
13811   int num_music_noconf = getMusicListSize_NoConf();
13812   int num_music = getMusicListSize();
13813   int num_sounds = getSoundListSize();
13814   struct FileInfo *music, *sound;
13815   struct MusicFileInfo *next, **new;
13816
13817   int i;
13818
13819   while (music_file_info != NULL)
13820   {
13821     next = music_file_info->next;
13822
13823     checked_free(music_file_info->basename);
13824
13825     checked_free(music_file_info->title_header);
13826     checked_free(music_file_info->artist_header);
13827     checked_free(music_file_info->album_header);
13828     checked_free(music_file_info->year_header);
13829     checked_free(music_file_info->played_header);
13830
13831     checked_free(music_file_info->title);
13832     checked_free(music_file_info->artist);
13833     checked_free(music_file_info->album);
13834     checked_free(music_file_info->year);
13835     checked_free(music_file_info->played);
13836
13837     free(music_file_info);
13838
13839     music_file_info = next;
13840   }
13841
13842   new = &music_file_info;
13843
13844   // get (configured or unconfigured) music file info for all levels
13845   for (i = leveldir_current->first_level;
13846        i <= leveldir_current->last_level; i++)
13847   {
13848     int music_nr;
13849
13850     if (levelset.music[i] != MUS_UNDEFINED)
13851     {
13852       // get music file info for configured level music
13853       music_nr = levelset.music[i];
13854     }
13855     else if (num_music_noconf > 0)
13856     {
13857       // get music file info for unconfigured level music
13858       int level_pos = i - leveldir_current->first_level;
13859
13860       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13861     }
13862     else
13863     {
13864       continue;
13865     }
13866
13867     char *basename = getMusicInfoEntryFilename(music_nr);
13868
13869     if (basename == NULL)
13870       continue;
13871
13872     if (!music_info_listed(music_file_info, basename))
13873     {
13874       *new = get_music_file_info(basename, music_nr);
13875
13876       if (*new != NULL)
13877         new = &(*new)->next;
13878     }
13879   }
13880
13881   // get music file info for all remaining configured music files
13882   for (i = 0; i < num_music; i++)
13883   {
13884     music = getMusicListEntry(i);
13885
13886     if (music->filename == NULL)
13887       continue;
13888
13889     if (strEqual(music->filename, UNDEFINED_FILENAME))
13890       continue;
13891
13892     // a configured file may be not recognized as music
13893     if (!FileIsMusic(music->filename))
13894       continue;
13895
13896     if (!music_info_listed(music_file_info, music->filename))
13897     {
13898       *new = get_music_file_info(music->filename, i);
13899
13900       if (*new != NULL)
13901         new = &(*new)->next;
13902     }
13903   }
13904
13905   // get sound file info for all configured sound files
13906   for (i = 0; i < num_sounds; i++)
13907   {
13908     sound = getSoundListEntry(i);
13909
13910     if (sound->filename == NULL)
13911       continue;
13912
13913     if (strEqual(sound->filename, UNDEFINED_FILENAME))
13914       continue;
13915
13916     // a configured file may be not recognized as sound
13917     if (!FileIsSound(sound->filename))
13918       continue;
13919
13920     if (!sound_info_listed(music_file_info, sound->filename))
13921     {
13922       *new = get_sound_file_info(sound->filename, i);
13923       if (*new != NULL)
13924         new = &(*new)->next;
13925     }
13926   }
13927
13928   // add pointers to previous list nodes
13929
13930   struct MusicFileInfo *node = music_file_info;
13931
13932   while (node != NULL)
13933   {
13934     if (node->next)
13935       node->next->prev = node;
13936
13937     node = node->next;
13938   }
13939 }
13940
13941 static void add_helpanim_entry(int element, int action, int direction,
13942                                int delay, int *num_list_entries)
13943 {
13944   struct HelpAnimInfo *new_list_entry;
13945   (*num_list_entries)++;
13946
13947   helpanim_info =
13948     checked_realloc(helpanim_info,
13949                     *num_list_entries * sizeof(struct HelpAnimInfo));
13950   new_list_entry = &helpanim_info[*num_list_entries - 1];
13951
13952   new_list_entry->element = element;
13953   new_list_entry->action = action;
13954   new_list_entry->direction = direction;
13955   new_list_entry->delay = delay;
13956 }
13957
13958 static void print_unknown_token(char *filename, char *token, int token_nr)
13959 {
13960   if (token_nr == 0)
13961   {
13962     Warn("---");
13963     Warn("unknown token(s) found in config file:");
13964     Warn("- config file: '%s'", filename);
13965   }
13966
13967   Warn("- token: '%s'", token);
13968 }
13969
13970 static void print_unknown_token_end(int token_nr)
13971 {
13972   if (token_nr > 0)
13973     Warn("---");
13974 }
13975
13976 void LoadHelpAnimInfo(void)
13977 {
13978   char *filename = getHelpAnimFilename();
13979   SetupFileList *setup_file_list = NULL, *list;
13980   SetupFileHash *element_hash, *action_hash, *direction_hash;
13981   int num_list_entries = 0;
13982   int num_unknown_tokens = 0;
13983   int i;
13984
13985   if (fileExists(filename))
13986     setup_file_list = loadSetupFileList(filename);
13987
13988   if (setup_file_list == NULL)
13989   {
13990     // use reliable default values from static configuration
13991     SetupFileList *insert_ptr;
13992
13993     insert_ptr = setup_file_list =
13994       newSetupFileList(helpanim_config[0].token,
13995                        helpanim_config[0].value);
13996
13997     for (i = 1; helpanim_config[i].token; i++)
13998       insert_ptr = addListEntry(insert_ptr,
13999                                 helpanim_config[i].token,
14000                                 helpanim_config[i].value);
14001   }
14002
14003   element_hash   = newSetupFileHash();
14004   action_hash    = newSetupFileHash();
14005   direction_hash = newSetupFileHash();
14006
14007   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14008     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14009
14010   for (i = 0; i < NUM_ACTIONS; i++)
14011     setHashEntry(action_hash, element_action_info[i].suffix,
14012                  i_to_a(element_action_info[i].value));
14013
14014   // do not store direction index (bit) here, but direction value!
14015   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14016     setHashEntry(direction_hash, element_direction_info[i].suffix,
14017                  i_to_a(1 << element_direction_info[i].value));
14018
14019   for (list = setup_file_list; list != NULL; list = list->next)
14020   {
14021     char *element_token, *action_token, *direction_token;
14022     char *element_value, *action_value, *direction_value;
14023     int delay = atoi(list->value);
14024
14025     if (strEqual(list->token, "end"))
14026     {
14027       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14028
14029       continue;
14030     }
14031
14032     /* first try to break element into element/action/direction parts;
14033        if this does not work, also accept combined "element[.act][.dir]"
14034        elements (like "dynamite.active"), which are unique elements */
14035
14036     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14037     {
14038       element_value = getHashEntry(element_hash, list->token);
14039       if (element_value != NULL)        // element found
14040         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14041                            &num_list_entries);
14042       else
14043       {
14044         // no further suffixes found -- this is not an element
14045         print_unknown_token(filename, list->token, num_unknown_tokens++);
14046       }
14047
14048       continue;
14049     }
14050
14051     // token has format "<prefix>.<something>"
14052
14053     action_token = strchr(list->token, '.');    // suffix may be action ...
14054     direction_token = action_token;             // ... or direction
14055
14056     element_token = getStringCopy(list->token);
14057     *strchr(element_token, '.') = '\0';
14058
14059     element_value = getHashEntry(element_hash, element_token);
14060
14061     if (element_value == NULL)          // this is no element
14062     {
14063       element_value = getHashEntry(element_hash, list->token);
14064       if (element_value != NULL)        // combined element found
14065         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14066                            &num_list_entries);
14067       else
14068         print_unknown_token(filename, list->token, num_unknown_tokens++);
14069
14070       free(element_token);
14071
14072       continue;
14073     }
14074
14075     action_value = getHashEntry(action_hash, action_token);
14076
14077     if (action_value != NULL)           // action found
14078     {
14079       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14080                     &num_list_entries);
14081
14082       free(element_token);
14083
14084       continue;
14085     }
14086
14087     direction_value = getHashEntry(direction_hash, direction_token);
14088
14089     if (direction_value != NULL)        // direction found
14090     {
14091       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14092                          &num_list_entries);
14093
14094       free(element_token);
14095
14096       continue;
14097     }
14098
14099     if (strchr(action_token + 1, '.') == NULL)
14100     {
14101       // no further suffixes found -- this is not an action nor direction
14102
14103       element_value = getHashEntry(element_hash, list->token);
14104       if (element_value != NULL)        // combined element found
14105         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14106                            &num_list_entries);
14107       else
14108         print_unknown_token(filename, list->token, num_unknown_tokens++);
14109
14110       free(element_token);
14111
14112       continue;
14113     }
14114
14115     // token has format "<prefix>.<suffix>.<something>"
14116
14117     direction_token = strchr(action_token + 1, '.');
14118
14119     action_token = getStringCopy(action_token);
14120     *strchr(action_token + 1, '.') = '\0';
14121
14122     action_value = getHashEntry(action_hash, action_token);
14123
14124     if (action_value == NULL)           // this is no action
14125     {
14126       element_value = getHashEntry(element_hash, list->token);
14127       if (element_value != NULL)        // combined element found
14128         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14129                            &num_list_entries);
14130       else
14131         print_unknown_token(filename, list->token, num_unknown_tokens++);
14132
14133       free(element_token);
14134       free(action_token);
14135
14136       continue;
14137     }
14138
14139     direction_value = getHashEntry(direction_hash, direction_token);
14140
14141     if (direction_value != NULL)        // direction found
14142     {
14143       add_helpanim_entry(atoi(element_value), atoi(action_value),
14144                          atoi(direction_value), delay, &num_list_entries);
14145
14146       free(element_token);
14147       free(action_token);
14148
14149       continue;
14150     }
14151
14152     // this is no direction
14153
14154     element_value = getHashEntry(element_hash, list->token);
14155     if (element_value != NULL)          // combined element found
14156       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14157                          &num_list_entries);
14158     else
14159       print_unknown_token(filename, list->token, num_unknown_tokens++);
14160
14161     free(element_token);
14162     free(action_token);
14163   }
14164
14165   print_unknown_token_end(num_unknown_tokens);
14166
14167   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14168   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14169
14170   freeSetupFileList(setup_file_list);
14171   freeSetupFileHash(element_hash);
14172   freeSetupFileHash(action_hash);
14173   freeSetupFileHash(direction_hash);
14174
14175 #if 0
14176   for (i = 0; i < num_list_entries; i++)
14177     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14178           EL_NAME(helpanim_info[i].element),
14179           helpanim_info[i].element,
14180           helpanim_info[i].action,
14181           helpanim_info[i].direction,
14182           helpanim_info[i].delay);
14183 #endif
14184 }
14185
14186 void LoadHelpTextInfo(void)
14187 {
14188   char *filename = getHelpTextFilename();
14189   int i;
14190
14191   if (helptext_info != NULL)
14192   {
14193     freeSetupFileHash(helptext_info);
14194     helptext_info = NULL;
14195   }
14196
14197   if (fileExists(filename))
14198     helptext_info = loadSetupFileHash(filename);
14199
14200   if (helptext_info == NULL)
14201   {
14202     // use reliable default values from static configuration
14203     helptext_info = newSetupFileHash();
14204
14205     for (i = 0; helptext_config[i].token; i++)
14206       setHashEntry(helptext_info,
14207                    helptext_config[i].token,
14208                    helptext_config[i].value);
14209   }
14210
14211 #if 0
14212   BEGIN_HASH_ITERATION(helptext_info, itr)
14213   {
14214     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14215           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14216   }
14217   END_HASH_ITERATION(hash, itr)
14218 #endif
14219 }
14220
14221
14222 // ----------------------------------------------------------------------------
14223 // convert levels
14224 // ----------------------------------------------------------------------------
14225
14226 #define MAX_NUM_CONVERT_LEVELS          1000
14227
14228 void ConvertLevels(void)
14229 {
14230   static LevelDirTree *convert_leveldir = NULL;
14231   static int convert_level_nr = -1;
14232   static int num_levels_handled = 0;
14233   static int num_levels_converted = 0;
14234   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14235   int i;
14236
14237   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14238                                                global.convert_leveldir);
14239
14240   if (convert_leveldir == NULL)
14241     Fail("no such level identifier: '%s'", global.convert_leveldir);
14242
14243   leveldir_current = convert_leveldir;
14244
14245   if (global.convert_level_nr != -1)
14246   {
14247     convert_leveldir->first_level = global.convert_level_nr;
14248     convert_leveldir->last_level  = global.convert_level_nr;
14249   }
14250
14251   convert_level_nr = convert_leveldir->first_level;
14252
14253   PrintLine("=", 79);
14254   Print("Converting levels\n");
14255   PrintLine("-", 79);
14256   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14257   Print("Level series name:       '%s'\n", convert_leveldir->name);
14258   Print("Level series author:     '%s'\n", convert_leveldir->author);
14259   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14260   PrintLine("=", 79);
14261   Print("\n");
14262
14263   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14264     levels_failed[i] = FALSE;
14265
14266   while (convert_level_nr <= convert_leveldir->last_level)
14267   {
14268     char *level_filename;
14269     boolean new_level;
14270
14271     level_nr = convert_level_nr++;
14272
14273     Print("Level %03d: ", level_nr);
14274
14275     LoadLevel(level_nr);
14276     if (level.no_level_file || level.no_valid_file)
14277     {
14278       Print("(no level)\n");
14279       continue;
14280     }
14281
14282     Print("converting level ... ");
14283
14284 #if 0
14285     // special case: conversion of some EMC levels as requested by ACME
14286     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14287 #endif
14288
14289     level_filename = getDefaultLevelFilename(level_nr);
14290     new_level = !fileExists(level_filename);
14291
14292     if (new_level)
14293     {
14294       SaveLevel(level_nr);
14295
14296       num_levels_converted++;
14297
14298       Print("converted.\n");
14299     }
14300     else
14301     {
14302       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14303         levels_failed[level_nr] = TRUE;
14304
14305       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14306     }
14307
14308     num_levels_handled++;
14309   }
14310
14311   Print("\n");
14312   PrintLine("=", 79);
14313   Print("Number of levels handled: %d\n", num_levels_handled);
14314   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14315          (num_levels_handled ?
14316           num_levels_converted * 100 / num_levels_handled : 0));
14317   PrintLine("-", 79);
14318   Print("Summary (for automatic parsing by scripts):\n");
14319   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14320          convert_leveldir->identifier, num_levels_converted,
14321          num_levels_handled,
14322          (num_levels_handled ?
14323           num_levels_converted * 100 / num_levels_handled : 0));
14324
14325   if (num_levels_handled != num_levels_converted)
14326   {
14327     Print(", FAILED:");
14328     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14329       if (levels_failed[i])
14330         Print(" %03d", i);
14331   }
14332
14333   Print("\n");
14334   PrintLine("=", 79);
14335
14336   CloseAllAndExit(0);
14337 }
14338
14339
14340 // ----------------------------------------------------------------------------
14341 // create and save images for use in level sketches (raw BMP format)
14342 // ----------------------------------------------------------------------------
14343
14344 void CreateLevelSketchImages(void)
14345 {
14346   Bitmap *bitmap1;
14347   Bitmap *bitmap2;
14348   int i;
14349
14350   InitElementPropertiesGfxElement();
14351
14352   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14353   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14354
14355   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14356   {
14357     int element = getMappedElement(i);
14358     char basename1[16];
14359     char basename2[16];
14360     char *filename1;
14361     char *filename2;
14362
14363     sprintf(basename1, "%04d.bmp", i);
14364     sprintf(basename2, "%04ds.bmp", i);
14365
14366     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14367     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14368
14369     DrawSizedElement(0, 0, element, TILESIZE);
14370     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14371
14372     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14373       Fail("cannot save level sketch image file '%s'", filename1);
14374
14375     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14376     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14377
14378     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14379       Fail("cannot save level sketch image file '%s'", filename2);
14380
14381     free(filename1);
14382     free(filename2);
14383
14384     // create corresponding SQL statements (for normal and small images)
14385     if (i < 1000)
14386     {
14387       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14388       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14389     }
14390
14391     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14392     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14393
14394     // optional: create content for forum level sketch demonstration post
14395     if (options.debug)
14396       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14397   }
14398
14399   FreeBitmap(bitmap1);
14400   FreeBitmap(bitmap2);
14401
14402   if (options.debug)
14403     fprintf(stderr, "\n");
14404
14405   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14406
14407   CloseAllAndExit(0);
14408 }
14409
14410
14411 // ----------------------------------------------------------------------------
14412 // create and save images for element collecting animations (raw BMP format)
14413 // ----------------------------------------------------------------------------
14414
14415 static boolean createCollectImage(int element)
14416 {
14417   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14418 }
14419
14420 void CreateCollectElementImages(void)
14421 {
14422   int i, j;
14423   int num_steps = 8;
14424   int anim_frames = num_steps - 1;
14425   int tile_size = TILESIZE;
14426   int anim_width  = tile_size * anim_frames;
14427   int anim_height = tile_size;
14428   int num_collect_images = 0;
14429   int pos_collect_images = 0;
14430
14431   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14432     if (createCollectImage(i))
14433       num_collect_images++;
14434
14435   Info("Creating %d element collecting animation images ...",
14436        num_collect_images);
14437
14438   int dst_width  = anim_width * 2;
14439   int dst_height = anim_height * num_collect_images / 2;
14440   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14441   char *basename_bmp = "RocksCollect.bmp";
14442   char *basename_png = "RocksCollect.png";
14443   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14444   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14445   int len_filename_bmp = strlen(filename_bmp);
14446   int len_filename_png = strlen(filename_png);
14447   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14448   char cmd_convert[max_command_len];
14449
14450   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14451            filename_bmp,
14452            filename_png);
14453
14454   // force using RGBA surface for destination bitmap
14455   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14456                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14457
14458   dst_bitmap->surface =
14459     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14460
14461   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14462   {
14463     if (!createCollectImage(i))
14464       continue;
14465
14466     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14467     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14468     int graphic = el2img(i);
14469     char *token_name = element_info[i].token_name;
14470     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14471     Bitmap *src_bitmap;
14472     int src_x, src_y;
14473
14474     Info("- creating collecting image for '%s' ...", token_name);
14475
14476     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14477
14478     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14479                tile_size, tile_size, 0, 0);
14480
14481     // force using RGBA surface for temporary bitmap (using transparent black)
14482     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14483                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14484
14485     tmp_bitmap->surface =
14486       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14487
14488     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14489
14490     for (j = 0; j < anim_frames; j++)
14491     {
14492       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14493       int frame_size = frame_size_final * num_steps;
14494       int offset = (tile_size - frame_size_final) / 2;
14495       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14496
14497       while (frame_size > frame_size_final)
14498       {
14499         frame_size /= 2;
14500
14501         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14502
14503         FreeBitmap(frame_bitmap);
14504
14505         frame_bitmap = half_bitmap;
14506       }
14507
14508       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14509                        frame_size_final, frame_size_final,
14510                        dst_x + j * tile_size + offset, dst_y + offset);
14511
14512       FreeBitmap(frame_bitmap);
14513     }
14514
14515     tmp_bitmap->surface_masked = NULL;
14516
14517     FreeBitmap(tmp_bitmap);
14518
14519     pos_collect_images++;
14520   }
14521
14522   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14523     Fail("cannot save element collecting image file '%s'", filename_bmp);
14524
14525   FreeBitmap(dst_bitmap);
14526
14527   Info("Converting image file from BMP to PNG ...");
14528
14529   if (system(cmd_convert) != 0)
14530     Fail("converting image file failed");
14531
14532   unlink(filename_bmp);
14533
14534   Info("Done.");
14535
14536   CloseAllAndExit(0);
14537 }
14538
14539
14540 // ----------------------------------------------------------------------------
14541 // create and save images for custom and group elements (raw BMP format)
14542 // ----------------------------------------------------------------------------
14543
14544 void CreateCustomElementImages(char *directory)
14545 {
14546   char *src_basename = "RocksCE-template.ilbm";
14547   char *dst_basename = "RocksCE.bmp";
14548   char *src_filename = getPath2(directory, src_basename);
14549   char *dst_filename = getPath2(directory, dst_basename);
14550   Bitmap *src_bitmap;
14551   Bitmap *bitmap;
14552   int yoffset_ce = 0;
14553   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14554   int i;
14555
14556   InitVideoDefaults();
14557
14558   ReCreateBitmap(&backbuffer, video.width, video.height);
14559
14560   src_bitmap = LoadImage(src_filename);
14561
14562   bitmap = CreateBitmap(TILEX * 16 * 2,
14563                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14564                         DEFAULT_DEPTH);
14565
14566   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14567   {
14568     int x = i % 16;
14569     int y = i / 16;
14570     int ii = i + 1;
14571     int j;
14572
14573     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14574                TILEX * x, TILEY * y + yoffset_ce);
14575
14576     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14577                TILEX, TILEY,
14578                TILEX * x + TILEX * 16,
14579                TILEY * y + yoffset_ce);
14580
14581     for (j = 2; j >= 0; j--)
14582     {
14583       int c = ii % 10;
14584
14585       BlitBitmap(src_bitmap, bitmap,
14586                  TILEX + c * 7, 0, 6, 10,
14587                  TILEX * x + 6 + j * 7,
14588                  TILEY * y + 11 + yoffset_ce);
14589
14590       BlitBitmap(src_bitmap, bitmap,
14591                  TILEX + c * 8, TILEY, 6, 10,
14592                  TILEX * 16 + TILEX * x + 6 + j * 8,
14593                  TILEY * y + 10 + yoffset_ce);
14594
14595       ii /= 10;
14596     }
14597   }
14598
14599   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14600   {
14601     int x = i % 16;
14602     int y = i / 16;
14603     int ii = i + 1;
14604     int j;
14605
14606     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14607                TILEX * x, TILEY * y + yoffset_ge);
14608
14609     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14610                TILEX, TILEY,
14611                TILEX * x + TILEX * 16,
14612                TILEY * y + yoffset_ge);
14613
14614     for (j = 1; j >= 0; j--)
14615     {
14616       int c = ii % 10;
14617
14618       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14619                  TILEX * x + 6 + j * 10,
14620                  TILEY * y + 11 + yoffset_ge);
14621
14622       BlitBitmap(src_bitmap, bitmap,
14623                  TILEX + c * 8, TILEY + 12, 6, 10,
14624                  TILEX * 16 + TILEX * x + 10 + j * 8,
14625                  TILEY * y + 10 + yoffset_ge);
14626
14627       ii /= 10;
14628     }
14629   }
14630
14631   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14632     Fail("cannot save CE graphics file '%s'", dst_filename);
14633
14634   FreeBitmap(bitmap);
14635
14636   CloseAllAndExit(0);
14637 }