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