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