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