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