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