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