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