changed "http" to "https" in URLs
[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  2       // 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     Error(ERR_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 static 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         Error(ERR_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     Error(ERR_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       Error(ERR_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       Error(ERR_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       Error(ERR_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     Error(ERR_WARN, "invalid custom element number %d", element);
2834
2835     ReadUnusedBytesFromFile(file, chunk_size - 2);
2836     return chunk_size;
2837   }
2838
2839   ei = &element_info[element];
2840
2841   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2842     ei->description[i] = getFile8Bit(file);
2843   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2844
2845   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2846
2847   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
2848
2849   ei->num_change_pages = getFile8Bit(file);
2850
2851   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2852   if (chunk_size_expected != chunk_size)
2853   {
2854     ReadUnusedBytesFromFile(file, chunk_size - 43);
2855     return chunk_size_expected;
2856   }
2857
2858   ei->ce_value_fixed_initial = getFile16BitBE(file);
2859   ei->ce_value_random_initial = getFile16BitBE(file);
2860   ei->use_last_ce_value = getFile8Bit(file);
2861
2862   ei->use_gfx_element = getFile8Bit(file);
2863   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2864
2865   ei->collect_score_initial = getFile8Bit(file);
2866   ei->collect_count_initial = getFile8Bit(file);
2867
2868   ei->drop_delay_fixed = getFile8Bit(file);
2869   ei->push_delay_fixed = getFile8Bit(file);
2870   ei->drop_delay_random = getFile8Bit(file);
2871   ei->push_delay_random = getFile8Bit(file);
2872   ei->move_delay_fixed = getFile16BitBE(file);
2873   ei->move_delay_random = getFile16BitBE(file);
2874
2875   // bits 0 - 15 of "move_pattern" ...
2876   ei->move_pattern = getFile16BitBE(file);
2877   ei->move_direction_initial = getFile8Bit(file);
2878   ei->move_stepsize = getFile8Bit(file);
2879
2880   ei->slippery_type = getFile8Bit(file);
2881
2882   for (y = 0; y < 3; y++)
2883     for (x = 0; x < 3; x++)
2884       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2885
2886   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2887   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2888   ei->move_leave_type = getFile8Bit(file);
2889
2890   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2891   ei->move_pattern |= (getFile16BitBE(file) << 16);
2892
2893   ei->access_direction = getFile8Bit(file);
2894
2895   ei->explosion_delay = getFile8Bit(file);
2896   ei->ignition_delay = getFile8Bit(file);
2897   ei->explosion_type = getFile8Bit(file);
2898
2899   // some free bytes for future custom property values and padding
2900   ReadUnusedBytesFromFile(file, 1);
2901
2902   // ---------- change page property values (48 bytes) ------------------------
2903
2904   setElementChangePages(ei, ei->num_change_pages);
2905
2906   for (i = 0; i < ei->num_change_pages; i++)
2907   {
2908     struct ElementChangeInfo *change = &ei->change_page[i];
2909     unsigned int event_bits;
2910
2911     // always start with reliable default values
2912     setElementChangeInfoToDefaults(change);
2913
2914     // bits 0 - 31 of "has_event[]" ...
2915     event_bits = getFile32BitBE(file);
2916     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2917       if (event_bits & (1 << j))
2918         change->has_event[j] = TRUE;
2919
2920     change->target_element = getMappedElement(getFile16BitBE(file));
2921
2922     change->delay_fixed = getFile16BitBE(file);
2923     change->delay_random = getFile16BitBE(file);
2924     change->delay_frames = getFile16BitBE(file);
2925
2926     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2927
2928     change->explode = getFile8Bit(file);
2929     change->use_target_content = getFile8Bit(file);
2930     change->only_if_complete = getFile8Bit(file);
2931     change->use_random_replace = getFile8Bit(file);
2932
2933     change->random_percentage = getFile8Bit(file);
2934     change->replace_when = getFile8Bit(file);
2935
2936     for (y = 0; y < 3; y++)
2937       for (x = 0; x < 3; x++)
2938         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2939
2940     change->can_change = getFile8Bit(file);
2941
2942     change->trigger_side = getFile8Bit(file);
2943
2944     change->trigger_player = getFile8Bit(file);
2945     change->trigger_page = getFile8Bit(file);
2946
2947     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2948                             CH_PAGE_ANY : (1 << change->trigger_page));
2949
2950     change->has_action = getFile8Bit(file);
2951     change->action_type = getFile8Bit(file);
2952     change->action_mode = getFile8Bit(file);
2953     change->action_arg = getFile16BitBE(file);
2954
2955     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2956     event_bits = getFile8Bit(file);
2957     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2958       if (event_bits & (1 << (j - 32)))
2959         change->has_event[j] = TRUE;
2960   }
2961
2962   // mark this custom element as modified
2963   ei->modified_settings = TRUE;
2964
2965   level->file_has_custom_elements = TRUE;
2966
2967   return chunk_size;
2968 }
2969
2970 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2971 {
2972   struct ElementInfo *ei;
2973   struct ElementGroupInfo *group;
2974   int element;
2975   int i;
2976
2977   element = getMappedElement(getFile16BitBE(file));
2978
2979   if (!IS_GROUP_ELEMENT(element))
2980   {
2981     Error(ERR_WARN, "invalid group element number %d", element);
2982
2983     ReadUnusedBytesFromFile(file, chunk_size - 2);
2984     return chunk_size;
2985   }
2986
2987   ei = &element_info[element];
2988
2989   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2990     ei->description[i] = getFile8Bit(file);
2991   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2992
2993   group = element_info[element].group;
2994
2995   group->num_elements = getFile8Bit(file);
2996
2997   ei->use_gfx_element = getFile8Bit(file);
2998   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2999
3000   group->choice_mode = getFile8Bit(file);
3001
3002   // some free bytes for future values and padding
3003   ReadUnusedBytesFromFile(file, 3);
3004
3005   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3006     group->element[i] = getMappedElement(getFile16BitBE(file));
3007
3008   // mark this group element as modified
3009   element_info[element].modified_settings = TRUE;
3010
3011   level->file_has_custom_elements = TRUE;
3012
3013   return chunk_size;
3014 }
3015
3016 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3017                                 int element, int real_element)
3018 {
3019   int micro_chunk_size = 0;
3020   int conf_type = getFile8Bit(file);
3021   int byte_mask = conf_type & CONF_MASK_BYTES;
3022   boolean element_found = FALSE;
3023   int i;
3024
3025   micro_chunk_size += 1;
3026
3027   if (byte_mask == CONF_MASK_MULTI_BYTES)
3028   {
3029     int num_bytes = getFile16BitBE(file);
3030     byte *buffer = checked_malloc(num_bytes);
3031
3032     ReadBytesFromFile(file, buffer, num_bytes);
3033
3034     for (i = 0; conf[i].data_type != -1; i++)
3035     {
3036       if (conf[i].element == element &&
3037           conf[i].conf_type == conf_type)
3038       {
3039         int data_type = conf[i].data_type;
3040         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3041         int max_num_entities = conf[i].max_num_entities;
3042
3043         if (num_entities > max_num_entities)
3044         {
3045           Error(ERR_WARN,
3046                 "truncating number of entities for element %d from %d to %d",
3047                 element, num_entities, max_num_entities);
3048
3049           num_entities = max_num_entities;
3050         }
3051
3052         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3053                                   data_type == TYPE_CONTENT_LIST))
3054         {
3055           // for element and content lists, zero entities are not allowed
3056           Error(ERR_WARN, "found empty list of entities for element %d",
3057                 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     Error(ERR_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     Error(ERR_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     Error(ERR_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       Error(ERR_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       Error(ERR_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       Error(ERR_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         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3474               chunk_name, filename);
3475         ReadUnusedBytesFromFile(file, chunk_size);
3476       }
3477       else if (chunk_info[i].size != -1 &&
3478                chunk_info[i].size != chunk_size)
3479       {
3480         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3481               chunk_size, chunk_name, filename);
3482         ReadUnusedBytesFromFile(file, chunk_size);
3483       }
3484       else
3485       {
3486         // call function to load this level chunk
3487         int chunk_size_expected =
3488           (chunk_info[i].loader)(file, chunk_size, level);
3489
3490         // the size of some chunks cannot be checked before reading other
3491         // chunks first (like "HEAD" and "BODY") that contain some header
3492         // information, so check them here
3493         if (chunk_size_expected != chunk_size)
3494         {
3495           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3496                 chunk_size, chunk_name, filename);
3497         }
3498       }
3499     }
3500   }
3501
3502   closeFile(file);
3503 }
3504
3505
3506 // ----------------------------------------------------------------------------
3507 // functions for loading EM level
3508 // ----------------------------------------------------------------------------
3509
3510 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3511 {
3512   static int ball_xy[8][2] =
3513   {
3514     { 0, 0 },
3515     { 1, 0 },
3516     { 2, 0 },
3517     { 0, 1 },
3518     { 2, 1 },
3519     { 0, 2 },
3520     { 1, 2 },
3521     { 2, 2 },
3522   };
3523   struct LevelInfo_EM *level_em = level->native_em_level;
3524   struct CAVE *cav = level_em->cav;
3525   int i, j, x, y;
3526
3527   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3528   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3529
3530   cav->time_seconds     = level->time;
3531   cav->gems_needed      = level->gems_needed;
3532
3533   cav->emerald_score    = level->score[SC_EMERALD];
3534   cav->diamond_score    = level->score[SC_DIAMOND];
3535   cav->alien_score      = level->score[SC_ROBOT];
3536   cav->tank_score       = level->score[SC_SPACESHIP];
3537   cav->bug_score        = level->score[SC_BUG];
3538   cav->eater_score      = level->score[SC_YAMYAM];
3539   cav->nut_score        = level->score[SC_NUT];
3540   cav->dynamite_score   = level->score[SC_DYNAMITE];
3541   cav->key_score        = level->score[SC_KEY];
3542   cav->exit_score       = level->score[SC_TIME_BONUS];
3543
3544   cav->num_eater_arrays = level->num_yamyam_contents;
3545
3546   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3547     for (y = 0; y < 3; y++)
3548       for (x = 0; x < 3; x++)
3549         cav->eater_array[i][y * 3 + x] =
3550           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3551
3552   cav->amoeba_time              = level->amoeba_speed;
3553   cav->wonderwall_time          = level->time_magic_wall;
3554   cav->wheel_time               = level->time_wheel;
3555
3556   cav->android_move_time        = level->android_move_time;
3557   cav->android_clone_time       = level->android_clone_time;
3558   cav->ball_random              = level->ball_random;
3559   cav->ball_active              = level->ball_active_initial;
3560   cav->ball_time                = level->ball_time;
3561   cav->num_ball_arrays          = level->num_ball_contents;
3562
3563   cav->lenses_score             = level->lenses_score;
3564   cav->magnify_score            = level->magnify_score;
3565   cav->slurp_score              = level->slurp_score;
3566
3567   cav->lenses_time              = level->lenses_time;
3568   cav->magnify_time             = level->magnify_time;
3569
3570   cav->wind_direction =
3571     map_direction_RND_to_EM(level->wind_direction_initial);
3572
3573   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3574     for (j = 0; j < 8; j++)
3575       cav->ball_array[i][j] =
3576         map_element_RND_to_EM_cave(level->ball_content[i].
3577                                    e[ball_xy[j][0]][ball_xy[j][1]]);
3578
3579   map_android_clone_elements_RND_to_EM(level);
3580
3581   // first fill the complete playfield with the empty space element
3582   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3583     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3584       cav->cave[x][y] = Cblank;
3585
3586   // then copy the real level contents from level file into the playfield
3587   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3588   {
3589     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3590
3591     if (level->field[x][y] == EL_AMOEBA_DEAD)
3592       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3593
3594     cav->cave[x][y] = new_element;
3595   }
3596
3597   for (i = 0; i < MAX_PLAYERS; i++)
3598   {
3599     cav->player_x[i] = -1;
3600     cav->player_y[i] = -1;
3601   }
3602
3603   // initialize player positions and delete players from the playfield
3604   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3605   {
3606     if (ELEM_IS_PLAYER(level->field[x][y]))
3607     {
3608       int player_nr = GET_PLAYER_NR(level->field[x][y]);
3609
3610       cav->player_x[player_nr] = x;
3611       cav->player_y[player_nr] = y;
3612
3613       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3614     }
3615   }
3616 }
3617
3618 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3619 {
3620   static int ball_xy[8][2] =
3621   {
3622     { 0, 0 },
3623     { 1, 0 },
3624     { 2, 0 },
3625     { 0, 1 },
3626     { 2, 1 },
3627     { 0, 2 },
3628     { 1, 2 },
3629     { 2, 2 },
3630   };
3631   struct LevelInfo_EM *level_em = level->native_em_level;
3632   struct CAVE *cav = level_em->cav;
3633   int i, j, x, y;
3634
3635   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
3636   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3637
3638   level->time        = cav->time_seconds;
3639   level->gems_needed = cav->gems_needed;
3640
3641   sprintf(level->name, "Level %d", level->file_info.nr);
3642
3643   level->score[SC_EMERALD]      = cav->emerald_score;
3644   level->score[SC_DIAMOND]      = cav->diamond_score;
3645   level->score[SC_ROBOT]        = cav->alien_score;
3646   level->score[SC_SPACESHIP]    = cav->tank_score;
3647   level->score[SC_BUG]          = cav->bug_score;
3648   level->score[SC_YAMYAM]       = cav->eater_score;
3649   level->score[SC_NUT]          = cav->nut_score;
3650   level->score[SC_DYNAMITE]     = cav->dynamite_score;
3651   level->score[SC_KEY]          = cav->key_score;
3652   level->score[SC_TIME_BONUS]   = cav->exit_score;
3653
3654   level->num_yamyam_contents    = cav->num_eater_arrays;
3655
3656   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3657     for (y = 0; y < 3; y++)
3658       for (x = 0; x < 3; x++)
3659         level->yamyam_content[i].e[x][y] =
3660           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3661
3662   level->amoeba_speed           = cav->amoeba_time;
3663   level->time_magic_wall        = cav->wonderwall_time;
3664   level->time_wheel             = cav->wheel_time;
3665
3666   level->android_move_time      = cav->android_move_time;
3667   level->android_clone_time     = cav->android_clone_time;
3668   level->ball_random            = cav->ball_random;
3669   level->ball_active_initial    = cav->ball_active;
3670   level->ball_time              = cav->ball_time;
3671   level->num_ball_contents      = cav->num_ball_arrays;
3672
3673   level->lenses_score           = cav->lenses_score;
3674   level->magnify_score          = cav->magnify_score;
3675   level->slurp_score            = cav->slurp_score;
3676
3677   level->lenses_time            = cav->lenses_time;
3678   level->magnify_time           = cav->magnify_time;
3679
3680   level->wind_direction_initial =
3681     map_direction_EM_to_RND(cav->wind_direction);
3682
3683   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3684     for (j = 0; j < 8; j++)
3685       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3686         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3687
3688   map_android_clone_elements_EM_to_RND(level);
3689
3690   // convert the playfield (some elements need special treatment)
3691   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3692   {
3693     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3694
3695     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3696       new_element = EL_AMOEBA_DEAD;
3697
3698     level->field[x][y] = new_element;
3699   }
3700
3701   for (i = 0; i < MAX_PLAYERS; i++)
3702   {
3703     // in case of all players set to the same field, use the first player
3704     int nr = MAX_PLAYERS - i - 1;
3705     int jx = cav->player_x[nr];
3706     int jy = cav->player_y[nr];
3707
3708     if (jx != -1 && jy != -1)
3709       level->field[jx][jy] = EL_PLAYER_1 + nr;
3710   }
3711 }
3712
3713
3714 // ----------------------------------------------------------------------------
3715 // functions for loading SP level
3716 // ----------------------------------------------------------------------------
3717
3718 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3719 {
3720   struct LevelInfo_SP *level_sp = level->native_sp_level;
3721   LevelInfoType *header = &level_sp->header;
3722   int i, x, y;
3723
3724   level_sp->width  = level->fieldx;
3725   level_sp->height = level->fieldy;
3726
3727   for (x = 0; x < level->fieldx; x++)
3728     for (y = 0; y < level->fieldy; y++)
3729       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3730
3731   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3732
3733   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3734     header->LevelTitle[i] = level->name[i];
3735   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3736
3737   header->InfotronsNeeded = level->gems_needed;
3738
3739   header->SpecialPortCount = 0;
3740
3741   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3742   {
3743     boolean gravity_port_found = FALSE;
3744     boolean gravity_port_valid = FALSE;
3745     int gravity_port_flag;
3746     int gravity_port_base_element;
3747     int element = level->field[x][y];
3748
3749     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3750         element <= EL_SP_GRAVITY_ON_PORT_UP)
3751     {
3752       gravity_port_found = TRUE;
3753       gravity_port_valid = TRUE;
3754       gravity_port_flag = 1;
3755       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3756     }
3757     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3758              element <= EL_SP_GRAVITY_OFF_PORT_UP)
3759     {
3760       gravity_port_found = TRUE;
3761       gravity_port_valid = TRUE;
3762       gravity_port_flag = 0;
3763       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3764     }
3765     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3766              element <= EL_SP_GRAVITY_PORT_UP)
3767     {
3768       // change R'n'D style gravity inverting special port to normal port
3769       // (there are no gravity inverting ports in native Supaplex engine)
3770
3771       gravity_port_found = TRUE;
3772       gravity_port_valid = FALSE;
3773       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3774     }
3775
3776     if (gravity_port_found)
3777     {
3778       if (gravity_port_valid &&
3779           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3780       {
3781         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3782
3783         port->PortLocation = (y * level->fieldx + x) * 2;
3784         port->Gravity = gravity_port_flag;
3785
3786         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3787
3788         header->SpecialPortCount++;
3789       }
3790       else
3791       {
3792         // change special gravity port to normal port
3793
3794         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3795       }
3796
3797       level_sp->playfield[x][y] = element - EL_SP_START;
3798     }
3799   }
3800 }
3801
3802 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3803 {
3804   struct LevelInfo_SP *level_sp = level->native_sp_level;
3805   LevelInfoType *header = &level_sp->header;
3806   boolean num_invalid_elements = 0;
3807   int i, j, x, y;
3808
3809   level->fieldx = level_sp->width;
3810   level->fieldy = level_sp->height;
3811
3812   for (x = 0; x < level->fieldx; x++)
3813   {
3814     for (y = 0; y < level->fieldy; y++)
3815     {
3816       int element_old = level_sp->playfield[x][y];
3817       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3818
3819       if (element_new == EL_UNKNOWN)
3820       {
3821         num_invalid_elements++;
3822
3823         Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3824               element_old, x, y);
3825       }
3826
3827       level->field[x][y] = element_new;
3828     }
3829   }
3830
3831   if (num_invalid_elements > 0)
3832     Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3833           (!options.debug ? " (use '--debug' for more details)" : ""));
3834
3835   for (i = 0; i < MAX_PLAYERS; i++)
3836     level->initial_player_gravity[i] =
3837       (header->InitialGravity == 1 ? TRUE : FALSE);
3838
3839   // skip leading spaces
3840   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3841     if (header->LevelTitle[i] != ' ')
3842       break;
3843
3844   // copy level title
3845   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3846     level->name[j] = header->LevelTitle[i];
3847   level->name[j] = '\0';
3848
3849   // cut trailing spaces
3850   for (; j > 0; j--)
3851     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3852       level->name[j - 1] = '\0';
3853
3854   level->gems_needed = header->InfotronsNeeded;
3855
3856   for (i = 0; i < header->SpecialPortCount; i++)
3857   {
3858     SpecialPortType *port = &header->SpecialPort[i];
3859     int port_location = port->PortLocation;
3860     int gravity = port->Gravity;
3861     int port_x, port_y, port_element;
3862
3863     port_x = (port_location / 2) % level->fieldx;
3864     port_y = (port_location / 2) / level->fieldx;
3865
3866     if (port_x < 0 || port_x >= level->fieldx ||
3867         port_y < 0 || port_y >= level->fieldy)
3868     {
3869       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3870             port_x, port_y);
3871
3872       continue;
3873     }
3874
3875     port_element = level->field[port_x][port_y];
3876
3877     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3878         port_element > EL_SP_GRAVITY_PORT_UP)
3879     {
3880       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3881
3882       continue;
3883     }
3884
3885     // change previous (wrong) gravity inverting special port to either
3886     // gravity enabling special port or gravity disabling special port
3887     level->field[port_x][port_y] +=
3888       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3889        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3890   }
3891
3892   // change special gravity ports without database entries to normal ports
3893   for (x = 0; x < level->fieldx; x++)
3894     for (y = 0; y < level->fieldy; y++)
3895       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3896           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3897         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3898
3899   level->time = 0;                      // no time limit
3900   level->amoeba_speed = 0;
3901   level->time_magic_wall = 0;
3902   level->time_wheel = 0;
3903   level->amoeba_content = EL_EMPTY;
3904
3905 #if 1
3906   // original Supaplex does not use score values -- use default values
3907 #else
3908   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3909     level->score[i] = 0;
3910 #endif
3911
3912   // there are no yamyams in supaplex levels
3913   for (i = 0; i < level->num_yamyam_contents; i++)
3914     for (x = 0; x < 3; x++)
3915       for (y = 0; y < 3; y++)
3916         level->yamyam_content[i].e[x][y] = EL_EMPTY;
3917 }
3918
3919 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3920 {
3921   struct LevelInfo_SP *level_sp = level->native_sp_level;
3922   struct DemoInfo_SP *demo = &level_sp->demo;
3923   int i, j;
3924
3925   // always start with reliable default values
3926   demo->is_available = FALSE;
3927   demo->length = 0;
3928
3929   if (TAPE_IS_EMPTY(tape))
3930     return;
3931
3932   demo->level_nr = tape.level_nr;       // (currently not used)
3933
3934   level_sp->header.DemoRandomSeed = tape.random_seed;
3935
3936   demo->length = 0;
3937
3938   for (i = 0; i < tape.length; i++)
3939   {
3940     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3941     int demo_repeat = tape.pos[i].delay;
3942     int demo_entries = (demo_repeat + 15) / 16;
3943
3944     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3945     {
3946       Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3947             SP_MAX_TAPE_LEN);
3948
3949       break;
3950     }
3951
3952     for (j = 0; j < demo_repeat / 16; j++)
3953       demo->data[demo->length++] = 0xf0 | demo_action;
3954
3955     if (demo_repeat % 16)
3956       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3957   }
3958
3959   demo->is_available = TRUE;
3960 }
3961
3962 static void setTapeInfoToDefaults(void);
3963
3964 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3965 {
3966   struct LevelInfo_SP *level_sp = level->native_sp_level;
3967   struct DemoInfo_SP *demo = &level_sp->demo;
3968   char *filename = level->file_info.filename;
3969   int i;
3970
3971   // always start with reliable default values
3972   setTapeInfoToDefaults();
3973
3974   if (!demo->is_available)
3975     return;
3976
3977   tape.level_nr = demo->level_nr;       // (currently not used)
3978   tape.random_seed = level_sp->header.DemoRandomSeed;
3979
3980   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3981
3982   tape.counter = 0;
3983   tape.pos[tape.counter].delay = 0;
3984
3985   for (i = 0; i < demo->length; i++)
3986   {
3987     int demo_action = demo->data[i] & 0x0f;
3988     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3989     int tape_action = map_key_SP_to_RND(demo_action);
3990     int tape_repeat = demo_repeat + 1;
3991     byte action[MAX_TAPE_ACTIONS] = { tape_action };
3992     boolean success = 0;
3993     int j;
3994
3995     for (j = 0; j < tape_repeat; j++)
3996       success = TapeAddAction(action);
3997
3998     if (!success)
3999     {
4000       Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
4001             MAX_TAPE_LEN);
4002
4003       break;
4004     }
4005   }
4006
4007   TapeHaltRecording();
4008 }
4009
4010
4011 // ----------------------------------------------------------------------------
4012 // functions for loading MM level
4013 // ----------------------------------------------------------------------------
4014
4015 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4016 {
4017   struct LevelInfo_MM *level_mm = level->native_mm_level;
4018   int x, y;
4019
4020   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4021   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4022
4023   level_mm->time = level->time;
4024   level_mm->kettles_needed = level->gems_needed;
4025   level_mm->auto_count_kettles = level->auto_count_gems;
4026
4027   level_mm->laser_red = level->mm_laser_red;
4028   level_mm->laser_green = level->mm_laser_green;
4029   level_mm->laser_blue = level->mm_laser_blue;
4030
4031   strcpy(level_mm->name, level->name);
4032   strcpy(level_mm->author, level->author);
4033
4034   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4035   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4036   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4037   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4038   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4039
4040   level_mm->amoeba_speed = level->amoeba_speed;
4041   level_mm->time_fuse    = level->mm_time_fuse;
4042   level_mm->time_bomb    = level->mm_time_bomb;
4043   level_mm->time_ball    = level->mm_time_ball;
4044   level_mm->time_block   = level->mm_time_block;
4045
4046   for (x = 0; x < level->fieldx; x++)
4047     for (y = 0; y < level->fieldy; y++)
4048       Ur[x][y] =
4049         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4050 }
4051
4052 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4053 {
4054   struct LevelInfo_MM *level_mm = level->native_mm_level;
4055   int x, y;
4056
4057   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4058   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4059
4060   level->time = level_mm->time;
4061   level->gems_needed = level_mm->kettles_needed;
4062   level->auto_count_gems = level_mm->auto_count_kettles;
4063
4064   level->mm_laser_red = level_mm->laser_red;
4065   level->mm_laser_green = level_mm->laser_green;
4066   level->mm_laser_blue = level_mm->laser_blue;
4067
4068   strcpy(level->name, level_mm->name);
4069
4070   // only overwrite author from 'levelinfo.conf' if author defined in level
4071   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4072     strcpy(level->author, level_mm->author);
4073
4074   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4075   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4076   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4077   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4078   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4079
4080   level->amoeba_speed  = level_mm->amoeba_speed;
4081   level->mm_time_fuse  = level_mm->time_fuse;
4082   level->mm_time_bomb  = level_mm->time_bomb;
4083   level->mm_time_ball  = level_mm->time_ball;
4084   level->mm_time_block = level_mm->time_block;
4085
4086   for (x = 0; x < level->fieldx; x++)
4087     for (y = 0; y < level->fieldy; y++)
4088       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4089 }
4090
4091
4092 // ----------------------------------------------------------------------------
4093 // functions for loading DC level
4094 // ----------------------------------------------------------------------------
4095
4096 #define DC_LEVEL_HEADER_SIZE            344
4097
4098 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4099                                         boolean init)
4100 {
4101   static int last_data_encoded;
4102   static int offset1;
4103   static int offset2;
4104   int diff;
4105   int diff_hi, diff_lo;
4106   int data_hi, data_lo;
4107   unsigned short data_decoded;
4108
4109   if (init)
4110   {
4111     last_data_encoded = 0;
4112     offset1 = -1;
4113     offset2 = 0;
4114
4115     return 0;
4116   }
4117
4118   diff = data_encoded - last_data_encoded;
4119   diff_hi = diff & ~0xff;
4120   diff_lo = diff &  0xff;
4121
4122   offset2 += diff_lo;
4123
4124   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4125   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4126   data_hi = data_hi & 0xff00;
4127
4128   data_decoded = data_hi | data_lo;
4129
4130   last_data_encoded = data_encoded;
4131
4132   offset1 = (offset1 + 1) % 31;
4133   offset2 = offset2 & 0xff;
4134
4135   return data_decoded;
4136 }
4137
4138 static int getMappedElement_DC(int element)
4139 {
4140   switch (element)
4141   {
4142     case 0x0000:
4143       element = EL_ROCK;
4144       break;
4145
4146       // 0x0117 - 0x036e: (?)
4147       // EL_DIAMOND
4148
4149       // 0x042d - 0x0684: (?)
4150       // EL_EMERALD
4151
4152     case 0x06f1:
4153       element = EL_NUT;
4154       break;
4155
4156     case 0x074c:
4157       element = EL_BOMB;
4158       break;
4159
4160     case 0x07a4:
4161       element = EL_PEARL;
4162       break;
4163
4164     case 0x0823:
4165       element = EL_CRYSTAL;
4166       break;
4167
4168     case 0x0e77:        // quicksand (boulder)
4169       element = EL_QUICKSAND_FAST_FULL;
4170       break;
4171
4172     case 0x0e99:        // slow quicksand (boulder)
4173       element = EL_QUICKSAND_FULL;
4174       break;
4175
4176     case 0x0ed2:
4177       element = EL_EM_EXIT_OPEN;
4178       break;
4179
4180     case 0x0ee3:
4181       element = EL_EM_EXIT_CLOSED;
4182       break;
4183
4184     case 0x0eeb:
4185       element = EL_EM_STEEL_EXIT_OPEN;
4186       break;
4187
4188     case 0x0efc:
4189       element = EL_EM_STEEL_EXIT_CLOSED;
4190       break;
4191
4192     case 0x0f4f:        // dynamite (lit 1)
4193       element = EL_EM_DYNAMITE_ACTIVE;
4194       break;
4195
4196     case 0x0f57:        // dynamite (lit 2)
4197       element = EL_EM_DYNAMITE_ACTIVE;
4198       break;
4199
4200     case 0x0f5f:        // dynamite (lit 3)
4201       element = EL_EM_DYNAMITE_ACTIVE;
4202       break;
4203
4204     case 0x0f67:        // dynamite (lit 4)
4205       element = EL_EM_DYNAMITE_ACTIVE;
4206       break;
4207
4208     case 0x0f81:
4209     case 0x0f82:
4210     case 0x0f83:
4211     case 0x0f84:
4212       element = EL_AMOEBA_WET;
4213       break;
4214
4215     case 0x0f85:
4216       element = EL_AMOEBA_DROP;
4217       break;
4218
4219     case 0x0fb9:
4220       element = EL_DC_MAGIC_WALL;
4221       break;
4222
4223     case 0x0fd0:
4224       element = EL_SPACESHIP_UP;
4225       break;
4226
4227     case 0x0fd9:
4228       element = EL_SPACESHIP_DOWN;
4229       break;
4230
4231     case 0x0ff1:
4232       element = EL_SPACESHIP_LEFT;
4233       break;
4234
4235     case 0x0ff9:
4236       element = EL_SPACESHIP_RIGHT;
4237       break;
4238
4239     case 0x1057:
4240       element = EL_BUG_UP;
4241       break;
4242
4243     case 0x1060:
4244       element = EL_BUG_DOWN;
4245       break;
4246
4247     case 0x1078:
4248       element = EL_BUG_LEFT;
4249       break;
4250
4251     case 0x1080:
4252       element = EL_BUG_RIGHT;
4253       break;
4254
4255     case 0x10de:
4256       element = EL_MOLE_UP;
4257       break;
4258
4259     case 0x10e7:
4260       element = EL_MOLE_DOWN;
4261       break;
4262
4263     case 0x10ff:
4264       element = EL_MOLE_LEFT;
4265       break;
4266
4267     case 0x1107:
4268       element = EL_MOLE_RIGHT;
4269       break;
4270
4271     case 0x11c0:
4272       element = EL_ROBOT;
4273       break;
4274
4275     case 0x13f5:
4276       element = EL_YAMYAM;
4277       break;
4278
4279     case 0x1425:
4280       element = EL_SWITCHGATE_OPEN;
4281       break;
4282
4283     case 0x1426:
4284       element = EL_SWITCHGATE_CLOSED;
4285       break;
4286
4287     case 0x1437:
4288       element = EL_DC_SWITCHGATE_SWITCH_UP;
4289       break;
4290
4291     case 0x143a:
4292       element = EL_TIMEGATE_CLOSED;
4293       break;
4294
4295     case 0x144c:        // conveyor belt switch (green)
4296       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4297       break;
4298
4299     case 0x144f:        // conveyor belt switch (red)
4300       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4301       break;
4302
4303     case 0x1452:        // conveyor belt switch (blue)
4304       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4305       break;
4306
4307     case 0x145b:
4308       element = EL_CONVEYOR_BELT_3_MIDDLE;
4309       break;
4310
4311     case 0x1463:
4312       element = EL_CONVEYOR_BELT_3_LEFT;
4313       break;
4314
4315     case 0x146b:
4316       element = EL_CONVEYOR_BELT_3_RIGHT;
4317       break;
4318
4319     case 0x1473:
4320       element = EL_CONVEYOR_BELT_1_MIDDLE;
4321       break;
4322
4323     case 0x147b:
4324       element = EL_CONVEYOR_BELT_1_LEFT;
4325       break;
4326
4327     case 0x1483:
4328       element = EL_CONVEYOR_BELT_1_RIGHT;
4329       break;
4330
4331     case 0x148b:
4332       element = EL_CONVEYOR_BELT_4_MIDDLE;
4333       break;
4334
4335     case 0x1493:
4336       element = EL_CONVEYOR_BELT_4_LEFT;
4337       break;
4338
4339     case 0x149b:
4340       element = EL_CONVEYOR_BELT_4_RIGHT;
4341       break;
4342
4343     case 0x14ac:
4344       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4345       break;
4346
4347     case 0x14bd:
4348       element = EL_EXPANDABLE_WALL_VERTICAL;
4349       break;
4350
4351     case 0x14c6:
4352       element = EL_EXPANDABLE_WALL_ANY;
4353       break;
4354
4355     case 0x14ce:        // growing steel wall (left/right)
4356       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4357       break;
4358
4359     case 0x14df:        // growing steel wall (up/down)
4360       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4361       break;
4362
4363     case 0x14e8:        // growing steel wall (up/down/left/right)
4364       element = EL_EXPANDABLE_STEELWALL_ANY;
4365       break;
4366
4367     case 0x14e9:
4368       element = EL_SHIELD_DEADLY;
4369       break;
4370
4371     case 0x1501:
4372       element = EL_EXTRA_TIME;
4373       break;
4374
4375     case 0x154f:
4376       element = EL_ACID;
4377       break;
4378
4379     case 0x1577:
4380       element = EL_EMPTY_SPACE;
4381       break;
4382
4383     case 0x1578:        // quicksand (empty)
4384       element = EL_QUICKSAND_FAST_EMPTY;
4385       break;
4386
4387     case 0x1579:        // slow quicksand (empty)
4388       element = EL_QUICKSAND_EMPTY;
4389       break;
4390
4391       // 0x157c - 0x158b:
4392       // EL_SAND
4393
4394       // 0x1590 - 0x159f:
4395       // EL_DC_LANDMINE
4396
4397     case 0x15a0:
4398       element = EL_EM_DYNAMITE;
4399       break;
4400
4401     case 0x15a1:        // key (red)
4402       element = EL_EM_KEY_1;
4403       break;
4404
4405     case 0x15a2:        // key (yellow)
4406       element = EL_EM_KEY_2;
4407       break;
4408
4409     case 0x15a3:        // key (blue)
4410       element = EL_EM_KEY_4;
4411       break;
4412
4413     case 0x15a4:        // key (green)
4414       element = EL_EM_KEY_3;
4415       break;
4416
4417     case 0x15a5:        // key (white)
4418       element = EL_DC_KEY_WHITE;
4419       break;
4420
4421     case 0x15a6:
4422       element = EL_WALL_SLIPPERY;
4423       break;
4424
4425     case 0x15a7:
4426       element = EL_WALL;
4427       break;
4428
4429     case 0x15a8:        // wall (not round)
4430       element = EL_WALL;
4431       break;
4432
4433     case 0x15a9:        // (blue)
4434       element = EL_CHAR_A;
4435       break;
4436
4437     case 0x15aa:        // (blue)
4438       element = EL_CHAR_B;
4439       break;
4440
4441     case 0x15ab:        // (blue)
4442       element = EL_CHAR_C;
4443       break;
4444
4445     case 0x15ac:        // (blue)
4446       element = EL_CHAR_D;
4447       break;
4448
4449     case 0x15ad:        // (blue)
4450       element = EL_CHAR_E;
4451       break;
4452
4453     case 0x15ae:        // (blue)
4454       element = EL_CHAR_F;
4455       break;
4456
4457     case 0x15af:        // (blue)
4458       element = EL_CHAR_G;
4459       break;
4460
4461     case 0x15b0:        // (blue)
4462       element = EL_CHAR_H;
4463       break;
4464
4465     case 0x15b1:        // (blue)
4466       element = EL_CHAR_I;
4467       break;
4468
4469     case 0x15b2:        // (blue)
4470       element = EL_CHAR_J;
4471       break;
4472
4473     case 0x15b3:        // (blue)
4474       element = EL_CHAR_K;
4475       break;
4476
4477     case 0x15b4:        // (blue)
4478       element = EL_CHAR_L;
4479       break;
4480
4481     case 0x15b5:        // (blue)
4482       element = EL_CHAR_M;
4483       break;
4484
4485     case 0x15b6:        // (blue)
4486       element = EL_CHAR_N;
4487       break;
4488
4489     case 0x15b7:        // (blue)
4490       element = EL_CHAR_O;
4491       break;
4492
4493     case 0x15b8:        // (blue)
4494       element = EL_CHAR_P;
4495       break;
4496
4497     case 0x15b9:        // (blue)
4498       element = EL_CHAR_Q;
4499       break;
4500
4501     case 0x15ba:        // (blue)
4502       element = EL_CHAR_R;
4503       break;
4504
4505     case 0x15bb:        // (blue)
4506       element = EL_CHAR_S;
4507       break;
4508
4509     case 0x15bc:        // (blue)
4510       element = EL_CHAR_T;
4511       break;
4512
4513     case 0x15bd:        // (blue)
4514       element = EL_CHAR_U;
4515       break;
4516
4517     case 0x15be:        // (blue)
4518       element = EL_CHAR_V;
4519       break;
4520
4521     case 0x15bf:        // (blue)
4522       element = EL_CHAR_W;
4523       break;
4524
4525     case 0x15c0:        // (blue)
4526       element = EL_CHAR_X;
4527       break;
4528
4529     case 0x15c1:        // (blue)
4530       element = EL_CHAR_Y;
4531       break;
4532
4533     case 0x15c2:        // (blue)
4534       element = EL_CHAR_Z;
4535       break;
4536
4537     case 0x15c3:        // (blue)
4538       element = EL_CHAR_AUMLAUT;
4539       break;
4540
4541     case 0x15c4:        // (blue)
4542       element = EL_CHAR_OUMLAUT;
4543       break;
4544
4545     case 0x15c5:        // (blue)
4546       element = EL_CHAR_UUMLAUT;
4547       break;
4548
4549     case 0x15c6:        // (blue)
4550       element = EL_CHAR_0;
4551       break;
4552
4553     case 0x15c7:        // (blue)
4554       element = EL_CHAR_1;
4555       break;
4556
4557     case 0x15c8:        // (blue)
4558       element = EL_CHAR_2;
4559       break;
4560
4561     case 0x15c9:        // (blue)
4562       element = EL_CHAR_3;
4563       break;
4564
4565     case 0x15ca:        // (blue)
4566       element = EL_CHAR_4;
4567       break;
4568
4569     case 0x15cb:        // (blue)
4570       element = EL_CHAR_5;
4571       break;
4572
4573     case 0x15cc:        // (blue)
4574       element = EL_CHAR_6;
4575       break;
4576
4577     case 0x15cd:        // (blue)
4578       element = EL_CHAR_7;
4579       break;
4580
4581     case 0x15ce:        // (blue)
4582       element = EL_CHAR_8;
4583       break;
4584
4585     case 0x15cf:        // (blue)
4586       element = EL_CHAR_9;
4587       break;
4588
4589     case 0x15d0:        // (blue)
4590       element = EL_CHAR_PERIOD;
4591       break;
4592
4593     case 0x15d1:        // (blue)
4594       element = EL_CHAR_EXCLAM;
4595       break;
4596
4597     case 0x15d2:        // (blue)
4598       element = EL_CHAR_COLON;
4599       break;
4600
4601     case 0x15d3:        // (blue)
4602       element = EL_CHAR_LESS;
4603       break;
4604
4605     case 0x15d4:        // (blue)
4606       element = EL_CHAR_GREATER;
4607       break;
4608
4609     case 0x15d5:        // (blue)
4610       element = EL_CHAR_QUESTION;
4611       break;
4612
4613     case 0x15d6:        // (blue)
4614       element = EL_CHAR_COPYRIGHT;
4615       break;
4616
4617     case 0x15d7:        // (blue)
4618       element = EL_CHAR_UP;
4619       break;
4620
4621     case 0x15d8:        // (blue)
4622       element = EL_CHAR_DOWN;
4623       break;
4624
4625     case 0x15d9:        // (blue)
4626       element = EL_CHAR_BUTTON;
4627       break;
4628
4629     case 0x15da:        // (blue)
4630       element = EL_CHAR_PLUS;
4631       break;
4632
4633     case 0x15db:        // (blue)
4634       element = EL_CHAR_MINUS;
4635       break;
4636
4637     case 0x15dc:        // (blue)
4638       element = EL_CHAR_APOSTROPHE;
4639       break;
4640
4641     case 0x15dd:        // (blue)
4642       element = EL_CHAR_PARENLEFT;
4643       break;
4644
4645     case 0x15de:        // (blue)
4646       element = EL_CHAR_PARENRIGHT;
4647       break;
4648
4649     case 0x15df:        // (green)
4650       element = EL_CHAR_A;
4651       break;
4652
4653     case 0x15e0:        // (green)
4654       element = EL_CHAR_B;
4655       break;
4656
4657     case 0x15e1:        // (green)
4658       element = EL_CHAR_C;
4659       break;
4660
4661     case 0x15e2:        // (green)
4662       element = EL_CHAR_D;
4663       break;
4664
4665     case 0x15e3:        // (green)
4666       element = EL_CHAR_E;
4667       break;
4668
4669     case 0x15e4:        // (green)
4670       element = EL_CHAR_F;
4671       break;
4672
4673     case 0x15e5:        // (green)
4674       element = EL_CHAR_G;
4675       break;
4676
4677     case 0x15e6:        // (green)
4678       element = EL_CHAR_H;
4679       break;
4680
4681     case 0x15e7:        // (green)
4682       element = EL_CHAR_I;
4683       break;
4684
4685     case 0x15e8:        // (green)
4686       element = EL_CHAR_J;
4687       break;
4688
4689     case 0x15e9:        // (green)
4690       element = EL_CHAR_K;
4691       break;
4692
4693     case 0x15ea:        // (green)
4694       element = EL_CHAR_L;
4695       break;
4696
4697     case 0x15eb:        // (green)
4698       element = EL_CHAR_M;
4699       break;
4700
4701     case 0x15ec:        // (green)
4702       element = EL_CHAR_N;
4703       break;
4704
4705     case 0x15ed:        // (green)
4706       element = EL_CHAR_O;
4707       break;
4708
4709     case 0x15ee:        // (green)
4710       element = EL_CHAR_P;
4711       break;
4712
4713     case 0x15ef:        // (green)
4714       element = EL_CHAR_Q;
4715       break;
4716
4717     case 0x15f0:        // (green)
4718       element = EL_CHAR_R;
4719       break;
4720
4721     case 0x15f1:        // (green)
4722       element = EL_CHAR_S;
4723       break;
4724
4725     case 0x15f2:        // (green)
4726       element = EL_CHAR_T;
4727       break;
4728
4729     case 0x15f3:        // (green)
4730       element = EL_CHAR_U;
4731       break;
4732
4733     case 0x15f4:        // (green)
4734       element = EL_CHAR_V;
4735       break;
4736
4737     case 0x15f5:        // (green)
4738       element = EL_CHAR_W;
4739       break;
4740
4741     case 0x15f6:        // (green)
4742       element = EL_CHAR_X;
4743       break;
4744
4745     case 0x15f7:        // (green)
4746       element = EL_CHAR_Y;
4747       break;
4748
4749     case 0x15f8:        // (green)
4750       element = EL_CHAR_Z;
4751       break;
4752
4753     case 0x15f9:        // (green)
4754       element = EL_CHAR_AUMLAUT;
4755       break;
4756
4757     case 0x15fa:        // (green)
4758       element = EL_CHAR_OUMLAUT;
4759       break;
4760
4761     case 0x15fb:        // (green)
4762       element = EL_CHAR_UUMLAUT;
4763       break;
4764
4765     case 0x15fc:        // (green)
4766       element = EL_CHAR_0;
4767       break;
4768
4769     case 0x15fd:        // (green)
4770       element = EL_CHAR_1;
4771       break;
4772
4773     case 0x15fe:        // (green)
4774       element = EL_CHAR_2;
4775       break;
4776
4777     case 0x15ff:        // (green)
4778       element = EL_CHAR_3;
4779       break;
4780
4781     case 0x1600:        // (green)
4782       element = EL_CHAR_4;
4783       break;
4784
4785     case 0x1601:        // (green)
4786       element = EL_CHAR_5;
4787       break;
4788
4789     case 0x1602:        // (green)
4790       element = EL_CHAR_6;
4791       break;
4792
4793     case 0x1603:        // (green)
4794       element = EL_CHAR_7;
4795       break;
4796
4797     case 0x1604:        // (green)
4798       element = EL_CHAR_8;
4799       break;
4800
4801     case 0x1605:        // (green)
4802       element = EL_CHAR_9;
4803       break;
4804
4805     case 0x1606:        // (green)
4806       element = EL_CHAR_PERIOD;
4807       break;
4808
4809     case 0x1607:        // (green)
4810       element = EL_CHAR_EXCLAM;
4811       break;
4812
4813     case 0x1608:        // (green)
4814       element = EL_CHAR_COLON;
4815       break;
4816
4817     case 0x1609:        // (green)
4818       element = EL_CHAR_LESS;
4819       break;
4820
4821     case 0x160a:        // (green)
4822       element = EL_CHAR_GREATER;
4823       break;
4824
4825     case 0x160b:        // (green)
4826       element = EL_CHAR_QUESTION;
4827       break;
4828
4829     case 0x160c:        // (green)
4830       element = EL_CHAR_COPYRIGHT;
4831       break;
4832
4833     case 0x160d:        // (green)
4834       element = EL_CHAR_UP;
4835       break;
4836
4837     case 0x160e:        // (green)
4838       element = EL_CHAR_DOWN;
4839       break;
4840
4841     case 0x160f:        // (green)
4842       element = EL_CHAR_BUTTON;
4843       break;
4844
4845     case 0x1610:        // (green)
4846       element = EL_CHAR_PLUS;
4847       break;
4848
4849     case 0x1611:        // (green)
4850       element = EL_CHAR_MINUS;
4851       break;
4852
4853     case 0x1612:        // (green)
4854       element = EL_CHAR_APOSTROPHE;
4855       break;
4856
4857     case 0x1613:        // (green)
4858       element = EL_CHAR_PARENLEFT;
4859       break;
4860
4861     case 0x1614:        // (green)
4862       element = EL_CHAR_PARENRIGHT;
4863       break;
4864
4865     case 0x1615:        // (blue steel)
4866       element = EL_STEEL_CHAR_A;
4867       break;
4868
4869     case 0x1616:        // (blue steel)
4870       element = EL_STEEL_CHAR_B;
4871       break;
4872
4873     case 0x1617:        // (blue steel)
4874       element = EL_STEEL_CHAR_C;
4875       break;
4876
4877     case 0x1618:        // (blue steel)
4878       element = EL_STEEL_CHAR_D;
4879       break;
4880
4881     case 0x1619:        // (blue steel)
4882       element = EL_STEEL_CHAR_E;
4883       break;
4884
4885     case 0x161a:        // (blue steel)
4886       element = EL_STEEL_CHAR_F;
4887       break;
4888
4889     case 0x161b:        // (blue steel)
4890       element = EL_STEEL_CHAR_G;
4891       break;
4892
4893     case 0x161c:        // (blue steel)
4894       element = EL_STEEL_CHAR_H;
4895       break;
4896
4897     case 0x161d:        // (blue steel)
4898       element = EL_STEEL_CHAR_I;
4899       break;
4900
4901     case 0x161e:        // (blue steel)
4902       element = EL_STEEL_CHAR_J;
4903       break;
4904
4905     case 0x161f:        // (blue steel)
4906       element = EL_STEEL_CHAR_K;
4907       break;
4908
4909     case 0x1620:        // (blue steel)
4910       element = EL_STEEL_CHAR_L;
4911       break;
4912
4913     case 0x1621:        // (blue steel)
4914       element = EL_STEEL_CHAR_M;
4915       break;
4916
4917     case 0x1622:        // (blue steel)
4918       element = EL_STEEL_CHAR_N;
4919       break;
4920
4921     case 0x1623:        // (blue steel)
4922       element = EL_STEEL_CHAR_O;
4923       break;
4924
4925     case 0x1624:        // (blue steel)
4926       element = EL_STEEL_CHAR_P;
4927       break;
4928
4929     case 0x1625:        // (blue steel)
4930       element = EL_STEEL_CHAR_Q;
4931       break;
4932
4933     case 0x1626:        // (blue steel)
4934       element = EL_STEEL_CHAR_R;
4935       break;
4936
4937     case 0x1627:        // (blue steel)
4938       element = EL_STEEL_CHAR_S;
4939       break;
4940
4941     case 0x1628:        // (blue steel)
4942       element = EL_STEEL_CHAR_T;
4943       break;
4944
4945     case 0x1629:        // (blue steel)
4946       element = EL_STEEL_CHAR_U;
4947       break;
4948
4949     case 0x162a:        // (blue steel)
4950       element = EL_STEEL_CHAR_V;
4951       break;
4952
4953     case 0x162b:        // (blue steel)
4954       element = EL_STEEL_CHAR_W;
4955       break;
4956
4957     case 0x162c:        // (blue steel)
4958       element = EL_STEEL_CHAR_X;
4959       break;
4960
4961     case 0x162d:        // (blue steel)
4962       element = EL_STEEL_CHAR_Y;
4963       break;
4964
4965     case 0x162e:        // (blue steel)
4966       element = EL_STEEL_CHAR_Z;
4967       break;
4968
4969     case 0x162f:        // (blue steel)
4970       element = EL_STEEL_CHAR_AUMLAUT;
4971       break;
4972
4973     case 0x1630:        // (blue steel)
4974       element = EL_STEEL_CHAR_OUMLAUT;
4975       break;
4976
4977     case 0x1631:        // (blue steel)
4978       element = EL_STEEL_CHAR_UUMLAUT;
4979       break;
4980
4981     case 0x1632:        // (blue steel)
4982       element = EL_STEEL_CHAR_0;
4983       break;
4984
4985     case 0x1633:        // (blue steel)
4986       element = EL_STEEL_CHAR_1;
4987       break;
4988
4989     case 0x1634:        // (blue steel)
4990       element = EL_STEEL_CHAR_2;
4991       break;
4992
4993     case 0x1635:        // (blue steel)
4994       element = EL_STEEL_CHAR_3;
4995       break;
4996
4997     case 0x1636:        // (blue steel)
4998       element = EL_STEEL_CHAR_4;
4999       break;
5000
5001     case 0x1637:        // (blue steel)
5002       element = EL_STEEL_CHAR_5;
5003       break;
5004
5005     case 0x1638:        // (blue steel)
5006       element = EL_STEEL_CHAR_6;
5007       break;
5008
5009     case 0x1639:        // (blue steel)
5010       element = EL_STEEL_CHAR_7;
5011       break;
5012
5013     case 0x163a:        // (blue steel)
5014       element = EL_STEEL_CHAR_8;
5015       break;
5016
5017     case 0x163b:        // (blue steel)
5018       element = EL_STEEL_CHAR_9;
5019       break;
5020
5021     case 0x163c:        // (blue steel)
5022       element = EL_STEEL_CHAR_PERIOD;
5023       break;
5024
5025     case 0x163d:        // (blue steel)
5026       element = EL_STEEL_CHAR_EXCLAM;
5027       break;
5028
5029     case 0x163e:        // (blue steel)
5030       element = EL_STEEL_CHAR_COLON;
5031       break;
5032
5033     case 0x163f:        // (blue steel)
5034       element = EL_STEEL_CHAR_LESS;
5035       break;
5036
5037     case 0x1640:        // (blue steel)
5038       element = EL_STEEL_CHAR_GREATER;
5039       break;
5040
5041     case 0x1641:        // (blue steel)
5042       element = EL_STEEL_CHAR_QUESTION;
5043       break;
5044
5045     case 0x1642:        // (blue steel)
5046       element = EL_STEEL_CHAR_COPYRIGHT;
5047       break;
5048
5049     case 0x1643:        // (blue steel)
5050       element = EL_STEEL_CHAR_UP;
5051       break;
5052
5053     case 0x1644:        // (blue steel)
5054       element = EL_STEEL_CHAR_DOWN;
5055       break;
5056
5057     case 0x1645:        // (blue steel)
5058       element = EL_STEEL_CHAR_BUTTON;
5059       break;
5060
5061     case 0x1646:        // (blue steel)
5062       element = EL_STEEL_CHAR_PLUS;
5063       break;
5064
5065     case 0x1647:        // (blue steel)
5066       element = EL_STEEL_CHAR_MINUS;
5067       break;
5068
5069     case 0x1648:        // (blue steel)
5070       element = EL_STEEL_CHAR_APOSTROPHE;
5071       break;
5072
5073     case 0x1649:        // (blue steel)
5074       element = EL_STEEL_CHAR_PARENLEFT;
5075       break;
5076
5077     case 0x164a:        // (blue steel)
5078       element = EL_STEEL_CHAR_PARENRIGHT;
5079       break;
5080
5081     case 0x164b:        // (green steel)
5082       element = EL_STEEL_CHAR_A;
5083       break;
5084
5085     case 0x164c:        // (green steel)
5086       element = EL_STEEL_CHAR_B;
5087       break;
5088
5089     case 0x164d:        // (green steel)
5090       element = EL_STEEL_CHAR_C;
5091       break;
5092
5093     case 0x164e:        // (green steel)
5094       element = EL_STEEL_CHAR_D;
5095       break;
5096
5097     case 0x164f:        // (green steel)
5098       element = EL_STEEL_CHAR_E;
5099       break;
5100
5101     case 0x1650:        // (green steel)
5102       element = EL_STEEL_CHAR_F;
5103       break;
5104
5105     case 0x1651:        // (green steel)
5106       element = EL_STEEL_CHAR_G;
5107       break;
5108
5109     case 0x1652:        // (green steel)
5110       element = EL_STEEL_CHAR_H;
5111       break;
5112
5113     case 0x1653:        // (green steel)
5114       element = EL_STEEL_CHAR_I;
5115       break;
5116
5117     case 0x1654:        // (green steel)
5118       element = EL_STEEL_CHAR_J;
5119       break;
5120
5121     case 0x1655:        // (green steel)
5122       element = EL_STEEL_CHAR_K;
5123       break;
5124
5125     case 0x1656:        // (green steel)
5126       element = EL_STEEL_CHAR_L;
5127       break;
5128
5129     case 0x1657:        // (green steel)
5130       element = EL_STEEL_CHAR_M;
5131       break;
5132
5133     case 0x1658:        // (green steel)
5134       element = EL_STEEL_CHAR_N;
5135       break;
5136
5137     case 0x1659:        // (green steel)
5138       element = EL_STEEL_CHAR_O;
5139       break;
5140
5141     case 0x165a:        // (green steel)
5142       element = EL_STEEL_CHAR_P;
5143       break;
5144
5145     case 0x165b:        // (green steel)
5146       element = EL_STEEL_CHAR_Q;
5147       break;
5148
5149     case 0x165c:        // (green steel)
5150       element = EL_STEEL_CHAR_R;
5151       break;
5152
5153     case 0x165d:        // (green steel)
5154       element = EL_STEEL_CHAR_S;
5155       break;
5156
5157     case 0x165e:        // (green steel)
5158       element = EL_STEEL_CHAR_T;
5159       break;
5160
5161     case 0x165f:        // (green steel)
5162       element = EL_STEEL_CHAR_U;
5163       break;
5164
5165     case 0x1660:        // (green steel)
5166       element = EL_STEEL_CHAR_V;
5167       break;
5168
5169     case 0x1661:        // (green steel)
5170       element = EL_STEEL_CHAR_W;
5171       break;
5172
5173     case 0x1662:        // (green steel)
5174       element = EL_STEEL_CHAR_X;
5175       break;
5176
5177     case 0x1663:        // (green steel)
5178       element = EL_STEEL_CHAR_Y;
5179       break;
5180
5181     case 0x1664:        // (green steel)
5182       element = EL_STEEL_CHAR_Z;
5183       break;
5184
5185     case 0x1665:        // (green steel)
5186       element = EL_STEEL_CHAR_AUMLAUT;
5187       break;
5188
5189     case 0x1666:        // (green steel)
5190       element = EL_STEEL_CHAR_OUMLAUT;
5191       break;
5192
5193     case 0x1667:        // (green steel)
5194       element = EL_STEEL_CHAR_UUMLAUT;
5195       break;
5196
5197     case 0x1668:        // (green steel)
5198       element = EL_STEEL_CHAR_0;
5199       break;
5200
5201     case 0x1669:        // (green steel)
5202       element = EL_STEEL_CHAR_1;
5203       break;
5204
5205     case 0x166a:        // (green steel)
5206       element = EL_STEEL_CHAR_2;
5207       break;
5208
5209     case 0x166b:        // (green steel)
5210       element = EL_STEEL_CHAR_3;
5211       break;
5212
5213     case 0x166c:        // (green steel)
5214       element = EL_STEEL_CHAR_4;
5215       break;
5216
5217     case 0x166d:        // (green steel)
5218       element = EL_STEEL_CHAR_5;
5219       break;
5220
5221     case 0x166e:        // (green steel)
5222       element = EL_STEEL_CHAR_6;
5223       break;
5224
5225     case 0x166f:        // (green steel)
5226       element = EL_STEEL_CHAR_7;
5227       break;
5228
5229     case 0x1670:        // (green steel)
5230       element = EL_STEEL_CHAR_8;
5231       break;
5232
5233     case 0x1671:        // (green steel)
5234       element = EL_STEEL_CHAR_9;
5235       break;
5236
5237     case 0x1672:        // (green steel)
5238       element = EL_STEEL_CHAR_PERIOD;
5239       break;
5240
5241     case 0x1673:        // (green steel)
5242       element = EL_STEEL_CHAR_EXCLAM;
5243       break;
5244
5245     case 0x1674:        // (green steel)
5246       element = EL_STEEL_CHAR_COLON;
5247       break;
5248
5249     case 0x1675:        // (green steel)
5250       element = EL_STEEL_CHAR_LESS;
5251       break;
5252
5253     case 0x1676:        // (green steel)
5254       element = EL_STEEL_CHAR_GREATER;
5255       break;
5256
5257     case 0x1677:        // (green steel)
5258       element = EL_STEEL_CHAR_QUESTION;
5259       break;
5260
5261     case 0x1678:        // (green steel)
5262       element = EL_STEEL_CHAR_COPYRIGHT;
5263       break;
5264
5265     case 0x1679:        // (green steel)
5266       element = EL_STEEL_CHAR_UP;
5267       break;
5268
5269     case 0x167a:        // (green steel)
5270       element = EL_STEEL_CHAR_DOWN;
5271       break;
5272
5273     case 0x167b:        // (green steel)
5274       element = EL_STEEL_CHAR_BUTTON;
5275       break;
5276
5277     case 0x167c:        // (green steel)
5278       element = EL_STEEL_CHAR_PLUS;
5279       break;
5280
5281     case 0x167d:        // (green steel)
5282       element = EL_STEEL_CHAR_MINUS;
5283       break;
5284
5285     case 0x167e:        // (green steel)
5286       element = EL_STEEL_CHAR_APOSTROPHE;
5287       break;
5288
5289     case 0x167f:        // (green steel)
5290       element = EL_STEEL_CHAR_PARENLEFT;
5291       break;
5292
5293     case 0x1680:        // (green steel)
5294       element = EL_STEEL_CHAR_PARENRIGHT;
5295       break;
5296
5297     case 0x1681:        // gate (red)
5298       element = EL_EM_GATE_1;
5299       break;
5300
5301     case 0x1682:        // secret gate (red)
5302       element = EL_GATE_1_GRAY;
5303       break;
5304
5305     case 0x1683:        // gate (yellow)
5306       element = EL_EM_GATE_2;
5307       break;
5308
5309     case 0x1684:        // secret gate (yellow)
5310       element = EL_GATE_2_GRAY;
5311       break;
5312
5313     case 0x1685:        // gate (blue)
5314       element = EL_EM_GATE_4;
5315       break;
5316
5317     case 0x1686:        // secret gate (blue)
5318       element = EL_GATE_4_GRAY;
5319       break;
5320
5321     case 0x1687:        // gate (green)
5322       element = EL_EM_GATE_3;
5323       break;
5324
5325     case 0x1688:        // secret gate (green)
5326       element = EL_GATE_3_GRAY;
5327       break;
5328
5329     case 0x1689:        // gate (white)
5330       element = EL_DC_GATE_WHITE;
5331       break;
5332
5333     case 0x168a:        // secret gate (white)
5334       element = EL_DC_GATE_WHITE_GRAY;
5335       break;
5336
5337     case 0x168b:        // secret gate (no key)
5338       element = EL_DC_GATE_FAKE_GRAY;
5339       break;
5340
5341     case 0x168c:
5342       element = EL_ROBOT_WHEEL;
5343       break;
5344
5345     case 0x168d:
5346       element = EL_DC_TIMEGATE_SWITCH;
5347       break;
5348
5349     case 0x168e:
5350       element = EL_ACID_POOL_BOTTOM;
5351       break;
5352
5353     case 0x168f:
5354       element = EL_ACID_POOL_TOPLEFT;
5355       break;
5356
5357     case 0x1690:
5358       element = EL_ACID_POOL_TOPRIGHT;
5359       break;
5360
5361     case 0x1691:
5362       element = EL_ACID_POOL_BOTTOMLEFT;
5363       break;
5364
5365     case 0x1692:
5366       element = EL_ACID_POOL_BOTTOMRIGHT;
5367       break;
5368
5369     case 0x1693:
5370       element = EL_STEELWALL;
5371       break;
5372
5373     case 0x1694:
5374       element = EL_STEELWALL_SLIPPERY;
5375       break;
5376
5377     case 0x1695:        // steel wall (not round)
5378       element = EL_STEELWALL;
5379       break;
5380
5381     case 0x1696:        // steel wall (left)
5382       element = EL_DC_STEELWALL_1_LEFT;
5383       break;
5384
5385     case 0x1697:        // steel wall (bottom)
5386       element = EL_DC_STEELWALL_1_BOTTOM;
5387       break;
5388
5389     case 0x1698:        // steel wall (right)
5390       element = EL_DC_STEELWALL_1_RIGHT;
5391       break;
5392
5393     case 0x1699:        // steel wall (top)
5394       element = EL_DC_STEELWALL_1_TOP;
5395       break;
5396
5397     case 0x169a:        // steel wall (left/bottom)
5398       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5399       break;
5400
5401     case 0x169b:        // steel wall (right/bottom)
5402       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5403       break;
5404
5405     case 0x169c:        // steel wall (right/top)
5406       element = EL_DC_STEELWALL_1_TOPRIGHT;
5407       break;
5408
5409     case 0x169d:        // steel wall (left/top)
5410       element = EL_DC_STEELWALL_1_TOPLEFT;
5411       break;
5412
5413     case 0x169e:        // steel wall (right/bottom small)
5414       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5415       break;
5416
5417     case 0x169f:        // steel wall (left/bottom small)
5418       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5419       break;
5420
5421     case 0x16a0:        // steel wall (right/top small)
5422       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5423       break;
5424
5425     case 0x16a1:        // steel wall (left/top small)
5426       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5427       break;
5428
5429     case 0x16a2:        // steel wall (left/right)
5430       element = EL_DC_STEELWALL_1_VERTICAL;
5431       break;
5432
5433     case 0x16a3:        // steel wall (top/bottom)
5434       element = EL_DC_STEELWALL_1_HORIZONTAL;
5435       break;
5436
5437     case 0x16a4:        // steel wall 2 (left end)
5438       element = EL_DC_STEELWALL_2_LEFT;
5439       break;
5440
5441     case 0x16a5:        // steel wall 2 (right end)
5442       element = EL_DC_STEELWALL_2_RIGHT;
5443       break;
5444
5445     case 0x16a6:        // steel wall 2 (top end)
5446       element = EL_DC_STEELWALL_2_TOP;
5447       break;
5448
5449     case 0x16a7:        // steel wall 2 (bottom end)
5450       element = EL_DC_STEELWALL_2_BOTTOM;
5451       break;
5452
5453     case 0x16a8:        // steel wall 2 (left/right)
5454       element = EL_DC_STEELWALL_2_HORIZONTAL;
5455       break;
5456
5457     case 0x16a9:        // steel wall 2 (up/down)
5458       element = EL_DC_STEELWALL_2_VERTICAL;
5459       break;
5460
5461     case 0x16aa:        // steel wall 2 (mid)
5462       element = EL_DC_STEELWALL_2_MIDDLE;
5463       break;
5464
5465     case 0x16ab:
5466       element = EL_SIGN_EXCLAMATION;
5467       break;
5468
5469     case 0x16ac:
5470       element = EL_SIGN_RADIOACTIVITY;
5471       break;
5472
5473     case 0x16ad:
5474       element = EL_SIGN_STOP;
5475       break;
5476
5477     case 0x16ae:
5478       element = EL_SIGN_WHEELCHAIR;
5479       break;
5480
5481     case 0x16af:
5482       element = EL_SIGN_PARKING;
5483       break;
5484
5485     case 0x16b0:
5486       element = EL_SIGN_NO_ENTRY;
5487       break;
5488
5489     case 0x16b1:
5490       element = EL_SIGN_HEART;
5491       break;
5492
5493     case 0x16b2:
5494       element = EL_SIGN_GIVE_WAY;
5495       break;
5496
5497     case 0x16b3:
5498       element = EL_SIGN_ENTRY_FORBIDDEN;
5499       break;
5500
5501     case 0x16b4:
5502       element = EL_SIGN_EMERGENCY_EXIT;
5503       break;
5504
5505     case 0x16b5:
5506       element = EL_SIGN_YIN_YANG;
5507       break;
5508
5509     case 0x16b6:
5510       element = EL_WALL_EMERALD;
5511       break;
5512
5513     case 0x16b7:
5514       element = EL_WALL_DIAMOND;
5515       break;
5516
5517     case 0x16b8:
5518       element = EL_WALL_PEARL;
5519       break;
5520
5521     case 0x16b9:
5522       element = EL_WALL_CRYSTAL;
5523       break;
5524
5525     case 0x16ba:
5526       element = EL_INVISIBLE_WALL;
5527       break;
5528
5529     case 0x16bb:
5530       element = EL_INVISIBLE_STEELWALL;
5531       break;
5532
5533       // 0x16bc - 0x16cb:
5534       // EL_INVISIBLE_SAND
5535
5536     case 0x16cc:
5537       element = EL_LIGHT_SWITCH;
5538       break;
5539
5540     case 0x16cd:
5541       element = EL_ENVELOPE_1;
5542       break;
5543
5544     default:
5545       if (element >= 0x0117 && element <= 0x036e)       // (?)
5546         element = EL_DIAMOND;
5547       else if (element >= 0x042d && element <= 0x0684)  // (?)
5548         element = EL_EMERALD;
5549       else if (element >= 0x157c && element <= 0x158b)
5550         element = EL_SAND;
5551       else if (element >= 0x1590 && element <= 0x159f)
5552         element = EL_DC_LANDMINE;
5553       else if (element >= 0x16bc && element <= 0x16cb)
5554         element = EL_INVISIBLE_SAND;
5555       else
5556       {
5557         Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5558         element = EL_UNKNOWN;
5559       }
5560       break;
5561   }
5562
5563   return getMappedElement(element);
5564 }
5565
5566 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5567                                        int nr)
5568 {
5569   byte header[DC_LEVEL_HEADER_SIZE];
5570   int envelope_size;
5571   int envelope_header_pos = 62;
5572   int envelope_content_pos = 94;
5573   int level_name_pos = 251;
5574   int level_author_pos = 292;
5575   int envelope_header_len;
5576   int envelope_content_len;
5577   int level_name_len;
5578   int level_author_len;
5579   int fieldx, fieldy;
5580   int num_yamyam_contents;
5581   int i, x, y;
5582
5583   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
5584
5585   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5586   {
5587     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5588
5589     header[i * 2 + 0] = header_word >> 8;
5590     header[i * 2 + 1] = header_word & 0xff;
5591   }
5592
5593   // read some values from level header to check level decoding integrity
5594   fieldx = header[6] | (header[7] << 8);
5595   fieldy = header[8] | (header[9] << 8);
5596   num_yamyam_contents = header[60] | (header[61] << 8);
5597
5598   // do some simple sanity checks to ensure that level was correctly decoded
5599   if (fieldx < 1 || fieldx > 256 ||
5600       fieldy < 1 || fieldy > 256 ||
5601       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5602   {
5603     level->no_valid_file = TRUE;
5604
5605     Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5606
5607     return;
5608   }
5609
5610   // maximum envelope header size is 31 bytes
5611   envelope_header_len   = header[envelope_header_pos];
5612   // maximum envelope content size is 110 (156?) bytes
5613   envelope_content_len  = header[envelope_content_pos];
5614
5615   // maximum level title size is 40 bytes
5616   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5617   // maximum level author size is 30 (51?) bytes
5618   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5619
5620   envelope_size = 0;
5621
5622   for (i = 0; i < envelope_header_len; i++)
5623     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5624       level->envelope[0].text[envelope_size++] =
5625         header[envelope_header_pos + 1 + i];
5626
5627   if (envelope_header_len > 0 && envelope_content_len > 0)
5628   {
5629     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5630       level->envelope[0].text[envelope_size++] = '\n';
5631     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5632       level->envelope[0].text[envelope_size++] = '\n';
5633   }
5634
5635   for (i = 0; i < envelope_content_len; i++)
5636     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5637       level->envelope[0].text[envelope_size++] =
5638         header[envelope_content_pos + 1 + i];
5639
5640   level->envelope[0].text[envelope_size] = '\0';
5641
5642   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5643   level->envelope[0].ysize = 10;
5644   level->envelope[0].autowrap = TRUE;
5645   level->envelope[0].centered = TRUE;
5646
5647   for (i = 0; i < level_name_len; i++)
5648     level->name[i] = header[level_name_pos + 1 + i];
5649   level->name[level_name_len] = '\0';
5650
5651   for (i = 0; i < level_author_len; i++)
5652     level->author[i] = header[level_author_pos + 1 + i];
5653   level->author[level_author_len] = '\0';
5654
5655   num_yamyam_contents = header[60] | (header[61] << 8);
5656   level->num_yamyam_contents =
5657     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5658
5659   for (i = 0; i < num_yamyam_contents; i++)
5660   {
5661     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5662     {
5663       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5664       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5665
5666       if (i < MAX_ELEMENT_CONTENTS)
5667         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5668     }
5669   }
5670
5671   fieldx = header[6] | (header[7] << 8);
5672   fieldy = header[8] | (header[9] << 8);
5673   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5674   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5675
5676   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5677   {
5678     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5679     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5680
5681     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5682       level->field[x][y] = getMappedElement_DC(element_dc);
5683   }
5684
5685   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5686   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5687   level->field[x][y] = EL_PLAYER_1;
5688
5689   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5690   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5691   level->field[x][y] = EL_PLAYER_2;
5692
5693   level->gems_needed            = header[18] | (header[19] << 8);
5694
5695   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
5696   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
5697   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
5698   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
5699   level->score[SC_NUT]          = header[28] | (header[29] << 8);
5700   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
5701   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
5702   level->score[SC_BUG]          = header[34] | (header[35] << 8);
5703   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
5704   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
5705   level->score[SC_KEY]          = header[40] | (header[41] << 8);
5706   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
5707
5708   level->time                   = header[44] | (header[45] << 8);
5709
5710   level->amoeba_speed           = header[46] | (header[47] << 8);
5711   level->time_light             = header[48] | (header[49] << 8);
5712   level->time_timegate          = header[50] | (header[51] << 8);
5713   level->time_wheel             = header[52] | (header[53] << 8);
5714   level->time_magic_wall        = header[54] | (header[55] << 8);
5715   level->extra_time             = header[56] | (header[57] << 8);
5716   level->shield_normal_time     = header[58] | (header[59] << 8);
5717
5718   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5719   // can slip down from flat walls, like normal walls and steel walls
5720   level->em_slippery_gems = TRUE;
5721 }
5722
5723 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5724                                      struct LevelFileInfo *level_file_info,
5725                                      boolean level_info_only)
5726 {
5727   char *filename = level_file_info->filename;
5728   File *file;
5729   int num_magic_bytes = 8;
5730   char magic_bytes[num_magic_bytes + 1];
5731   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5732
5733   if (!(file = openFile(filename, MODE_READ)))
5734   {
5735     level->no_valid_file = TRUE;
5736
5737     if (!level_info_only)
5738       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5739
5740     return;
5741   }
5742
5743   // fseek(file, 0x0000, SEEK_SET);
5744
5745   if (level_file_info->packed)
5746   {
5747     // read "magic bytes" from start of file
5748     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5749       magic_bytes[0] = '\0';
5750
5751     // check "magic bytes" for correct file format
5752     if (!strPrefix(magic_bytes, "DC2"))
5753     {
5754       level->no_valid_file = TRUE;
5755
5756       Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5757             filename);
5758
5759       return;
5760     }
5761
5762     if (strPrefix(magic_bytes, "DC2Win95") ||
5763         strPrefix(magic_bytes, "DC2Win98"))
5764     {
5765       int position_first_level = 0x00fa;
5766       int extra_bytes = 4;
5767       int skip_bytes;
5768
5769       // advance file stream to first level inside the level package
5770       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5771
5772       // each block of level data is followed by block of non-level data
5773       num_levels_to_skip *= 2;
5774
5775       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5776       while (num_levels_to_skip >= 0)
5777       {
5778         // advance file stream to next level inside the level package
5779         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5780         {
5781           level->no_valid_file = TRUE;
5782
5783           Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5784                 filename);
5785
5786           return;
5787         }
5788
5789         // skip apparently unused extra bytes following each level
5790         ReadUnusedBytesFromFile(file, extra_bytes);
5791
5792         // read size of next level in level package
5793         skip_bytes = getFile32BitLE(file);
5794
5795         num_levels_to_skip--;
5796       }
5797     }
5798     else
5799     {
5800       level->no_valid_file = TRUE;
5801
5802       Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5803             filename);
5804
5805       return;
5806     }
5807   }
5808
5809   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5810
5811   closeFile(file);
5812 }
5813
5814
5815 // ----------------------------------------------------------------------------
5816 // functions for loading SB level
5817 // ----------------------------------------------------------------------------
5818
5819 int getMappedElement_SB(int element_ascii, boolean use_ces)
5820 {
5821   static struct
5822   {
5823     int ascii;
5824     int sb;
5825     int ce;
5826   }
5827   sb_element_mapping[] =
5828   {
5829     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
5830     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
5831     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
5832     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
5833     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
5834     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
5835     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
5836     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
5837
5838     { 0,   -1,                      -1          },
5839   };
5840
5841   int i;
5842
5843   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5844     if (element_ascii == sb_element_mapping[i].ascii)
5845       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5846
5847   return EL_UNDEFINED;
5848 }
5849
5850 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5851                                      struct LevelFileInfo *level_file_info,
5852                                      boolean level_info_only)
5853 {
5854   char *filename = level_file_info->filename;
5855   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5856   char last_comment[MAX_LINE_LEN];
5857   char level_name[MAX_LINE_LEN];
5858   char *line_ptr;
5859   File *file;
5860   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5861   boolean read_continued_line = FALSE;
5862   boolean reading_playfield = FALSE;
5863   boolean got_valid_playfield_line = FALSE;
5864   boolean invalid_playfield_char = FALSE;
5865   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5866   int file_level_nr = 0;
5867   int line_nr = 0;
5868   int x = 0, y = 0;             // initialized to make compilers happy
5869
5870   last_comment[0] = '\0';
5871   level_name[0] = '\0';
5872
5873   if (!(file = openFile(filename, MODE_READ)))
5874   {
5875     level->no_valid_file = TRUE;
5876
5877     if (!level_info_only)
5878       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5879
5880     return;
5881   }
5882
5883   while (!checkEndOfFile(file))
5884   {
5885     // level successfully read, but next level may follow here
5886     if (!got_valid_playfield_line && reading_playfield)
5887     {
5888       // read playfield from single level file -- skip remaining file
5889       if (!level_file_info->packed)
5890         break;
5891
5892       if (file_level_nr >= num_levels_to_skip)
5893         break;
5894
5895       file_level_nr++;
5896
5897       last_comment[0] = '\0';
5898       level_name[0] = '\0';
5899
5900       reading_playfield = FALSE;
5901     }
5902
5903     got_valid_playfield_line = FALSE;
5904
5905     // read next line of input file
5906     if (!getStringFromFile(file, line, MAX_LINE_LEN))
5907       break;
5908
5909     // check if line was completely read and is terminated by line break
5910     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5911       line_nr++;
5912
5913     // cut trailing line break (this can be newline and/or carriage return)
5914     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5915       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5916         *line_ptr = '\0';
5917
5918     // copy raw input line for later use (mainly debugging output)
5919     strcpy(line_raw, line);
5920
5921     if (read_continued_line)
5922     {
5923       // append new line to existing line, if there is enough space
5924       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5925         strcat(previous_line, line_ptr);
5926
5927       strcpy(line, previous_line);      // copy storage buffer to line
5928
5929       read_continued_line = FALSE;
5930     }
5931
5932     // if the last character is '\', continue at next line
5933     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5934     {
5935       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
5936       strcpy(previous_line, line);      // copy line to storage buffer
5937
5938       read_continued_line = TRUE;
5939
5940       continue;
5941     }
5942
5943     // skip empty lines
5944     if (line[0] == '\0')
5945       continue;
5946
5947     // extract comment text from comment line
5948     if (line[0] == ';')
5949     {
5950       for (line_ptr = line; *line_ptr; line_ptr++)
5951         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5952           break;
5953
5954       strcpy(last_comment, line_ptr);
5955
5956       continue;
5957     }
5958
5959     // extract level title text from line containing level title
5960     if (line[0] == '\'')
5961     {
5962       strcpy(level_name, &line[1]);
5963
5964       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5965         level_name[strlen(level_name) - 1] = '\0';
5966
5967       continue;
5968     }
5969
5970     // skip lines containing only spaces (or empty lines)
5971     for (line_ptr = line; *line_ptr; line_ptr++)
5972       if (*line_ptr != ' ')
5973         break;
5974     if (*line_ptr == '\0')
5975       continue;
5976
5977     // at this point, we have found a line containing part of a playfield
5978
5979     got_valid_playfield_line = TRUE;
5980
5981     if (!reading_playfield)
5982     {
5983       reading_playfield = TRUE;
5984       invalid_playfield_char = FALSE;
5985
5986       for (x = 0; x < MAX_LEV_FIELDX; x++)
5987         for (y = 0; y < MAX_LEV_FIELDY; y++)
5988           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5989
5990       level->fieldx = 0;
5991       level->fieldy = 0;
5992
5993       // start with topmost tile row
5994       y = 0;
5995     }
5996
5997     // skip playfield line if larger row than allowed
5998     if (y >= MAX_LEV_FIELDY)
5999       continue;
6000
6001     // start with leftmost tile column
6002     x = 0;
6003
6004     // read playfield elements from line
6005     for (line_ptr = line; *line_ptr; line_ptr++)
6006     {
6007       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6008
6009       // stop parsing playfield line if larger column than allowed
6010       if (x >= MAX_LEV_FIELDX)
6011         break;
6012
6013       if (mapped_sb_element == EL_UNDEFINED)
6014       {
6015         invalid_playfield_char = TRUE;
6016
6017         break;
6018       }
6019
6020       level->field[x][y] = mapped_sb_element;
6021
6022       // continue with next tile column
6023       x++;
6024
6025       level->fieldx = MAX(x, level->fieldx);
6026     }
6027
6028     if (invalid_playfield_char)
6029     {
6030       // if first playfield line, treat invalid lines as comment lines
6031       if (y == 0)
6032         reading_playfield = FALSE;
6033
6034       continue;
6035     }
6036
6037     // continue with next tile row
6038     y++;
6039   }
6040
6041   closeFile(file);
6042
6043   level->fieldy = y;
6044
6045   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6046   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6047
6048   if (!reading_playfield)
6049   {
6050     level->no_valid_file = TRUE;
6051
6052     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6053
6054     return;
6055   }
6056
6057   if (*level_name != '\0')
6058   {
6059     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6060     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6061   }
6062   else if (*last_comment != '\0')
6063   {
6064     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6065     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6066   }
6067   else
6068   {
6069     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6070   }
6071
6072   // set all empty fields beyond the border walls to invisible steel wall
6073   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6074   {
6075     if ((x == 0 || x == level->fieldx - 1 ||
6076          y == 0 || y == level->fieldy - 1) &&
6077         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6078       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6079                      level->field, level->fieldx, level->fieldy);
6080   }
6081
6082   // set special level settings for Sokoban levels
6083
6084   level->time = 0;
6085   level->use_step_counter = TRUE;
6086
6087   if (load_xsb_to_ces)
6088   {
6089     // special global settings can now be set in level template
6090
6091     level->use_custom_template = TRUE;
6092   }
6093 }
6094
6095
6096 // -------------------------------------------------------------------------
6097 // functions for handling native levels
6098 // -------------------------------------------------------------------------
6099
6100 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6101                                      struct LevelFileInfo *level_file_info,
6102                                      boolean level_info_only)
6103 {
6104   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6105     level->no_valid_file = TRUE;
6106 }
6107
6108 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6109                                      struct LevelFileInfo *level_file_info,
6110                                      boolean level_info_only)
6111 {
6112   int pos = 0;
6113
6114   // determine position of requested level inside level package
6115   if (level_file_info->packed)
6116     pos = level_file_info->nr - leveldir_current->first_level;
6117
6118   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6119     level->no_valid_file = TRUE;
6120 }
6121
6122 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6123                                      struct LevelFileInfo *level_file_info,
6124                                      boolean level_info_only)
6125 {
6126   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6127     level->no_valid_file = TRUE;
6128 }
6129
6130 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6131 {
6132   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6133     CopyNativeLevel_RND_to_EM(level);
6134   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6135     CopyNativeLevel_RND_to_SP(level);
6136   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6137     CopyNativeLevel_RND_to_MM(level);
6138 }
6139
6140 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6141 {
6142   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6143     CopyNativeLevel_EM_to_RND(level);
6144   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6145     CopyNativeLevel_SP_to_RND(level);
6146   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6147     CopyNativeLevel_MM_to_RND(level);
6148 }
6149
6150 void SaveNativeLevel(struct LevelInfo *level)
6151 {
6152   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6153   {
6154     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6155     char *filename = getLevelFilenameFromBasename(basename);
6156
6157     CopyNativeLevel_RND_to_SP(level);
6158     CopyNativeTape_RND_to_SP(level);
6159
6160     SaveNativeLevel_SP(filename);
6161   }
6162 }
6163
6164
6165 // ----------------------------------------------------------------------------
6166 // functions for loading generic level
6167 // ----------------------------------------------------------------------------
6168
6169 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6170                                   struct LevelFileInfo *level_file_info,
6171                                   boolean level_info_only)
6172 {
6173   // always start with reliable default values
6174   setLevelInfoToDefaults(level, level_info_only, TRUE);
6175
6176   switch (level_file_info->type)
6177   {
6178     case LEVEL_FILE_TYPE_RND:
6179       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6180       break;
6181
6182     case LEVEL_FILE_TYPE_EM:
6183       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6184       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6185       break;
6186
6187     case LEVEL_FILE_TYPE_SP:
6188       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6189       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6190       break;
6191
6192     case LEVEL_FILE_TYPE_MM:
6193       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6194       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6195       break;
6196
6197     case LEVEL_FILE_TYPE_DC:
6198       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6199       break;
6200
6201     case LEVEL_FILE_TYPE_SB:
6202       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6203       break;
6204
6205     default:
6206       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6207       break;
6208   }
6209
6210   // if level file is invalid, restore level structure to default values
6211   if (level->no_valid_file)
6212     setLevelInfoToDefaults(level, level_info_only, FALSE);
6213
6214   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6215     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6216
6217   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6218     CopyNativeLevel_Native_to_RND(level);
6219 }
6220
6221 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6222 {
6223   static struct LevelFileInfo level_file_info;
6224
6225   // always start with reliable default values
6226   setFileInfoToDefaults(&level_file_info);
6227
6228   level_file_info.nr = 0;                       // unknown level number
6229   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
6230
6231   setString(&level_file_info.filename, filename);
6232
6233   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6234 }
6235
6236 static void LoadLevel_InitVersion(struct LevelInfo *level)
6237 {
6238   int i, j;
6239
6240   if (leveldir_current == NULL)         // only when dumping level
6241     return;
6242
6243   // all engine modifications also valid for levels which use latest engine
6244   if (level->game_version < VERSION_IDENT(3,2,0,5))
6245   {
6246     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6247     level->score[SC_TIME_BONUS] /= 10;
6248   }
6249
6250   if (leveldir_current->latest_engine)
6251   {
6252     // ---------- use latest game engine --------------------------------------
6253
6254     /* For all levels which are forced to use the latest game engine version
6255        (normally all but user contributed, private and undefined levels), set
6256        the game engine version to the actual version; this allows for actual
6257        corrections in the game engine to take effect for existing, converted
6258        levels (from "classic" or other existing games) to make the emulation
6259        of the corresponding game more accurate, while (hopefully) not breaking
6260        existing levels created from other players. */
6261
6262     level->game_version = GAME_VERSION_ACTUAL;
6263
6264     /* Set special EM style gems behaviour: EM style gems slip down from
6265        normal, steel and growing wall. As this is a more fundamental change,
6266        it seems better to set the default behaviour to "off" (as it is more
6267        natural) and make it configurable in the level editor (as a property
6268        of gem style elements). Already existing converted levels (neither
6269        private nor contributed levels) are changed to the new behaviour. */
6270
6271     if (level->file_version < FILE_VERSION_2_0)
6272       level->em_slippery_gems = TRUE;
6273
6274     return;
6275   }
6276
6277   // ---------- use game engine the level was created with --------------------
6278
6279   /* For all levels which are not forced to use the latest game engine
6280      version (normally user contributed, private and undefined levels),
6281      use the version of the game engine the levels were created for.
6282
6283      Since 2.0.1, the game engine version is now directly stored
6284      in the level file (chunk "VERS"), so there is no need anymore
6285      to set the game version from the file version (except for old,
6286      pre-2.0 levels, where the game version is still taken from the
6287      file format version used to store the level -- see above). */
6288
6289   // player was faster than enemies in 1.0.0 and before
6290   if (level->file_version == FILE_VERSION_1_0)
6291     for (i = 0; i < MAX_PLAYERS; i++)
6292       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6293
6294   // default behaviour for EM style gems was "slippery" only in 2.0.1
6295   if (level->game_version == VERSION_IDENT(2,0,1,0))
6296     level->em_slippery_gems = TRUE;
6297
6298   // springs could be pushed over pits before (pre-release version) 2.2.0
6299   if (level->game_version < VERSION_IDENT(2,2,0,0))
6300     level->use_spring_bug = TRUE;
6301
6302   if (level->game_version < VERSION_IDENT(3,2,0,5))
6303   {
6304     // time orb caused limited time in endless time levels before 3.2.0-5
6305     level->use_time_orb_bug = TRUE;
6306
6307     // default behaviour for snapping was "no snap delay" before 3.2.0-5
6308     level->block_snap_field = FALSE;
6309
6310     // extra time score was same value as time left score before 3.2.0-5
6311     level->extra_time_score = level->score[SC_TIME_BONUS];
6312   }
6313
6314   if (level->game_version < VERSION_IDENT(3,2,0,7))
6315   {
6316     // default behaviour for snapping was "not continuous" before 3.2.0-7
6317     level->continuous_snapping = FALSE;
6318   }
6319
6320   // only few elements were able to actively move into acid before 3.1.0
6321   // trigger settings did not exist before 3.1.0; set to default "any"
6322   if (level->game_version < VERSION_IDENT(3,1,0,0))
6323   {
6324     // correct "can move into acid" settings (all zero in old levels)
6325
6326     level->can_move_into_acid_bits = 0; // nothing can move into acid
6327     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6328
6329     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6330     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6331     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6332     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6333
6334     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6335       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6336
6337     // correct trigger settings (stored as zero == "none" in old levels)
6338
6339     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6340     {
6341       int element = EL_CUSTOM_START + i;
6342       struct ElementInfo *ei = &element_info[element];
6343
6344       for (j = 0; j < ei->num_change_pages; j++)
6345       {
6346         struct ElementChangeInfo *change = &ei->change_page[j];
6347
6348         change->trigger_player = CH_PLAYER_ANY;
6349         change->trigger_page = CH_PAGE_ANY;
6350       }
6351     }
6352   }
6353
6354   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6355   {
6356     int element = EL_CUSTOM_256;
6357     struct ElementInfo *ei = &element_info[element];
6358     struct ElementChangeInfo *change = &ei->change_page[0];
6359
6360     /* This is needed to fix a problem that was caused by a bugfix in function
6361        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6362        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6363        not replace walkable elements, but instead just placed the player on it,
6364        without placing the Sokoban field under the player). Unfortunately, this
6365        breaks "Snake Bite" style levels when the snake is halfway through a door
6366        that just closes (the snake head is still alive and can be moved in this
6367        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6368        player (without Sokoban element) which then gets killed as designed). */
6369
6370     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6371          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6372         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6373       change->target_element = EL_PLAYER_1;
6374   }
6375
6376   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6377   if (level->game_version < VERSION_IDENT(3,2,5,0))
6378   {
6379     /* This is needed to fix a problem that was caused by a bugfix in function
6380        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6381        corrects the behaviour when a custom element changes to another custom
6382        element with a higher element number that has change actions defined.
6383        Normally, only one change per frame is allowed for custom elements.
6384        Therefore, it is checked if a custom element already changed in the
6385        current frame; if it did, subsequent changes are suppressed.
6386        Unfortunately, this is only checked for element changes, but not for
6387        change actions, which are still executed. As the function above loops
6388        through all custom elements from lower to higher, an element change
6389        resulting in a lower CE number won't be checked again, while a target
6390        element with a higher number will also be checked, and potential change
6391        actions will get executed for this CE, too (which is wrong), while
6392        further changes are ignored (which is correct). As this bugfix breaks
6393        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6394        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6395        behaviour for existing levels and tapes that make use of this bug */
6396
6397     level->use_action_after_change_bug = TRUE;
6398   }
6399
6400   // not centering level after relocating player was default only in 3.2.3
6401   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
6402     level->shifted_relocation = TRUE;
6403
6404   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6405   if (level->game_version < VERSION_IDENT(3,2,6,0))
6406     level->em_explodes_by_fire = TRUE;
6407
6408   // levels were solved by the first player entering an exit up to 4.1.0.0
6409   if (level->game_version <= VERSION_IDENT(4,1,0,0))
6410     level->solved_by_one_player = TRUE;
6411
6412   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6413   if (level->game_version < VERSION_IDENT(4,1,1,1))
6414     level->use_life_bugs = TRUE;
6415
6416   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6417   if (level->game_version < VERSION_IDENT(4,1,1,1))
6418     level->sb_objects_needed = FALSE;
6419 }
6420
6421 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6422 {
6423   int i, x, y;
6424
6425   // map elements that have changed in newer versions
6426   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6427                                                     level->game_version);
6428   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6429     for (x = 0; x < 3; x++)
6430       for (y = 0; y < 3; y++)
6431         level->yamyam_content[i].e[x][y] =
6432           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6433                                     level->game_version);
6434
6435 }
6436
6437 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6438 {
6439   int i, j;
6440
6441   // map custom element change events that have changed in newer versions
6442   // (these following values were accidentally changed in version 3.0.1)
6443   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6444   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6445   {
6446     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6447     {
6448       int element = EL_CUSTOM_START + i;
6449
6450       // order of checking and copying events to be mapped is important
6451       // (do not change the start and end value -- they are constant)
6452       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6453       {
6454         if (HAS_CHANGE_EVENT(element, j - 2))
6455         {
6456           SET_CHANGE_EVENT(element, j - 2, FALSE);
6457           SET_CHANGE_EVENT(element, j, TRUE);
6458         }
6459       }
6460
6461       // order of checking and copying events to be mapped is important
6462       // (do not change the start and end value -- they are constant)
6463       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6464       {
6465         if (HAS_CHANGE_EVENT(element, j - 1))
6466         {
6467           SET_CHANGE_EVENT(element, j - 1, FALSE);
6468           SET_CHANGE_EVENT(element, j, TRUE);
6469         }
6470       }
6471     }
6472   }
6473
6474   // initialize "can_change" field for old levels with only one change page
6475   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6476   {
6477     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6478     {
6479       int element = EL_CUSTOM_START + i;
6480
6481       if (CAN_CHANGE(element))
6482         element_info[element].change->can_change = TRUE;
6483     }
6484   }
6485
6486   // correct custom element values (for old levels without these options)
6487   if (level->game_version < VERSION_IDENT(3,1,1,0))
6488   {
6489     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6490     {
6491       int element = EL_CUSTOM_START + i;
6492       struct ElementInfo *ei = &element_info[element];
6493
6494       if (ei->access_direction == MV_NO_DIRECTION)
6495         ei->access_direction = MV_ALL_DIRECTIONS;
6496     }
6497   }
6498
6499   // correct custom element values (fix invalid values for all versions)
6500   if (1)
6501   {
6502     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6503     {
6504       int element = EL_CUSTOM_START + i;
6505       struct ElementInfo *ei = &element_info[element];
6506
6507       for (j = 0; j < ei->num_change_pages; j++)
6508       {
6509         struct ElementChangeInfo *change = &ei->change_page[j];
6510
6511         if (change->trigger_player == CH_PLAYER_NONE)
6512           change->trigger_player = CH_PLAYER_ANY;
6513
6514         if (change->trigger_side == CH_SIDE_NONE)
6515           change->trigger_side = CH_SIDE_ANY;
6516       }
6517     }
6518   }
6519
6520   // initialize "can_explode" field for old levels which did not store this
6521   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6522   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6523   {
6524     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6525     {
6526       int element = EL_CUSTOM_START + i;
6527
6528       if (EXPLODES_1X1_OLD(element))
6529         element_info[element].explosion_type = EXPLODES_1X1;
6530
6531       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6532                                              EXPLODES_SMASHED(element) ||
6533                                              EXPLODES_IMPACT(element)));
6534     }
6535   }
6536
6537   // correct previously hard-coded move delay values for maze runner style
6538   if (level->game_version < VERSION_IDENT(3,1,1,0))
6539   {
6540     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6541     {
6542       int element = EL_CUSTOM_START + i;
6543
6544       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6545       {
6546         // previously hard-coded and therefore ignored
6547         element_info[element].move_delay_fixed = 9;
6548         element_info[element].move_delay_random = 0;
6549       }
6550     }
6551   }
6552
6553   // set some other uninitialized values of custom elements in older levels
6554   if (level->game_version < VERSION_IDENT(3,1,0,0))
6555   {
6556     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6557     {
6558       int element = EL_CUSTOM_START + i;
6559
6560       element_info[element].access_direction = MV_ALL_DIRECTIONS;
6561
6562       element_info[element].explosion_delay = 17;
6563       element_info[element].ignition_delay = 8;
6564     }
6565   }
6566 }
6567
6568 static void LoadLevel_InitElements(struct LevelInfo *level)
6569 {
6570   LoadLevel_InitStandardElements(level);
6571
6572   if (level->file_has_custom_elements)
6573     LoadLevel_InitCustomElements(level);
6574
6575   // initialize element properties for level editor etc.
6576   InitElementPropertiesEngine(level->game_version);
6577   InitElementPropertiesGfxElement();
6578 }
6579
6580 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6581 {
6582   int x, y;
6583
6584   // map elements that have changed in newer versions
6585   for (y = 0; y < level->fieldy; y++)
6586     for (x = 0; x < level->fieldx; x++)
6587       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6588                                                      level->game_version);
6589
6590   // clear unused playfield data (nicer if level gets resized in editor)
6591   for (x = 0; x < MAX_LEV_FIELDX; x++)
6592     for (y = 0; y < MAX_LEV_FIELDY; y++)
6593       if (x >= level->fieldx || y >= level->fieldy)
6594         level->field[x][y] = EL_EMPTY;
6595
6596   // copy elements to runtime playfield array
6597   for (x = 0; x < MAX_LEV_FIELDX; x++)
6598     for (y = 0; y < MAX_LEV_FIELDY; y++)
6599       Feld[x][y] = level->field[x][y];
6600
6601   // initialize level size variables for faster access
6602   lev_fieldx = level->fieldx;
6603   lev_fieldy = level->fieldy;
6604
6605   // determine border element for this level
6606   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6607     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
6608   else
6609     SetBorderElement();
6610 }
6611
6612 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6613 {
6614   struct LevelFileInfo *level_file_info = &level->file_info;
6615
6616   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6617     CopyNativeLevel_RND_to_Native(level);
6618 }
6619
6620 static void LoadLevelTemplate_LoadAndInit(void)
6621 {
6622   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6623
6624   LoadLevel_InitVersion(&level_template);
6625   LoadLevel_InitElements(&level_template);
6626
6627   ActivateLevelTemplate();
6628 }
6629
6630 void LoadLevelTemplate(int nr)
6631 {
6632   if (!fileExists(getGlobalLevelTemplateFilename()))
6633   {
6634     Error(ERR_WARN, "no level template found for this level");
6635
6636     return;
6637   }
6638
6639   setLevelFileInfo(&level_template.file_info, nr);
6640
6641   LoadLevelTemplate_LoadAndInit();
6642 }
6643
6644 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6645 {
6646   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6647
6648   LoadLevelTemplate_LoadAndInit();
6649 }
6650
6651 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6652 {
6653   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6654
6655   if (level.use_custom_template)
6656   {
6657     if (network_level != NULL)
6658       LoadNetworkLevelTemplate(network_level);
6659     else
6660       LoadLevelTemplate(-1);
6661   }
6662
6663   LoadLevel_InitVersion(&level);
6664   LoadLevel_InitElements(&level);
6665   LoadLevel_InitPlayfield(&level);
6666
6667   LoadLevel_InitNativeEngines(&level);
6668 }
6669
6670 void LoadLevel(int nr)
6671 {
6672   SetLevelSetInfo(leveldir_current->identifier, nr);
6673
6674   setLevelFileInfo(&level.file_info, nr);
6675
6676   LoadLevel_LoadAndInit(NULL);
6677 }
6678
6679 void LoadLevelInfoOnly(int nr)
6680 {
6681   setLevelFileInfo(&level.file_info, nr);
6682
6683   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6684 }
6685
6686 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6687 {
6688   SetLevelSetInfo(network_level->leveldir_identifier,
6689                   network_level->file_info.nr);
6690
6691   copyLevelFileInfo(&network_level->file_info, &level.file_info);
6692
6693   LoadLevel_LoadAndInit(network_level);
6694 }
6695
6696 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6697 {
6698   int chunk_size = 0;
6699
6700   chunk_size += putFileVersion(file, level->file_version);
6701   chunk_size += putFileVersion(file, level->game_version);
6702
6703   return chunk_size;
6704 }
6705
6706 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6707 {
6708   int chunk_size = 0;
6709
6710   chunk_size += putFile16BitBE(file, level->creation_date.year);
6711   chunk_size += putFile8Bit(file,    level->creation_date.month);
6712   chunk_size += putFile8Bit(file,    level->creation_date.day);
6713
6714   return chunk_size;
6715 }
6716
6717 #if ENABLE_HISTORIC_CHUNKS
6718 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6719 {
6720   int i, x, y;
6721
6722   putFile8Bit(file, level->fieldx);
6723   putFile8Bit(file, level->fieldy);
6724
6725   putFile16BitBE(file, level->time);
6726   putFile16BitBE(file, level->gems_needed);
6727
6728   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6729     putFile8Bit(file, level->name[i]);
6730
6731   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6732     putFile8Bit(file, level->score[i]);
6733
6734   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6735     for (y = 0; y < 3; y++)
6736       for (x = 0; x < 3; x++)
6737         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6738                            level->yamyam_content[i].e[x][y]));
6739   putFile8Bit(file, level->amoeba_speed);
6740   putFile8Bit(file, level->time_magic_wall);
6741   putFile8Bit(file, level->time_wheel);
6742   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6743                      level->amoeba_content));
6744   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6745   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6746   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6747   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6748
6749   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6750
6751   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6752   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6753   putFile32BitBE(file, level->can_move_into_acid_bits);
6754   putFile8Bit(file, level->dont_collide_with_bits);
6755
6756   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6757   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6758
6759   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6760   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6761   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6762
6763   putFile8Bit(file, level->game_engine_type);
6764
6765   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6766 }
6767 #endif
6768
6769 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6770 {
6771   int chunk_size = 0;
6772   int i;
6773
6774   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6775     chunk_size += putFile8Bit(file, level->name[i]);
6776
6777   return chunk_size;
6778 }
6779
6780 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6781 {
6782   int chunk_size = 0;
6783   int i;
6784
6785   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6786     chunk_size += putFile8Bit(file, level->author[i]);
6787
6788   return chunk_size;
6789 }
6790
6791 #if ENABLE_HISTORIC_CHUNKS
6792 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6793 {
6794   int chunk_size = 0;
6795   int x, y;
6796
6797   for (y = 0; y < level->fieldy; y++)
6798     for (x = 0; x < level->fieldx; x++)
6799       if (level->encoding_16bit_field)
6800         chunk_size += putFile16BitBE(file, level->field[x][y]);
6801       else
6802         chunk_size += putFile8Bit(file, level->field[x][y]);
6803
6804   return chunk_size;
6805 }
6806 #endif
6807
6808 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6809 {
6810   int chunk_size = 0;
6811   int x, y;
6812
6813   for (y = 0; y < level->fieldy; y++) 
6814     for (x = 0; x < level->fieldx; x++) 
6815       chunk_size += putFile16BitBE(file, level->field[x][y]);
6816
6817   return chunk_size;
6818 }
6819
6820 #if ENABLE_HISTORIC_CHUNKS
6821 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6822 {
6823   int i, x, y;
6824
6825   putFile8Bit(file, EL_YAMYAM);
6826   putFile8Bit(file, level->num_yamyam_contents);
6827   putFile8Bit(file, 0);
6828   putFile8Bit(file, 0);
6829
6830   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6831     for (y = 0; y < 3; y++)
6832       for (x = 0; x < 3; x++)
6833         if (level->encoding_16bit_field)
6834           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6835         else
6836           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6837 }
6838 #endif
6839
6840 #if ENABLE_HISTORIC_CHUNKS
6841 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6842 {
6843   int i, x, y;
6844   int num_contents, content_xsize, content_ysize;
6845   int content_array[MAX_ELEMENT_CONTENTS][3][3];
6846
6847   if (element == EL_YAMYAM)
6848   {
6849     num_contents = level->num_yamyam_contents;
6850     content_xsize = 3;
6851     content_ysize = 3;
6852
6853     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6854       for (y = 0; y < 3; y++)
6855         for (x = 0; x < 3; x++)
6856           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6857   }
6858   else if (element == EL_BD_AMOEBA)
6859   {
6860     num_contents = 1;
6861     content_xsize = 1;
6862     content_ysize = 1;
6863
6864     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6865       for (y = 0; y < 3; y++)
6866         for (x = 0; x < 3; x++)
6867           content_array[i][x][y] = EL_EMPTY;
6868     content_array[0][0][0] = level->amoeba_content;
6869   }
6870   else
6871   {
6872     // chunk header already written -- write empty chunk data
6873     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6874
6875     Error(ERR_WARN, "cannot save content for element '%d'", element);
6876     return;
6877   }
6878
6879   putFile16BitBE(file, element);
6880   putFile8Bit(file, num_contents);
6881   putFile8Bit(file, content_xsize);
6882   putFile8Bit(file, content_ysize);
6883
6884   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6885
6886   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6887     for (y = 0; y < 3; y++)
6888       for (x = 0; x < 3; x++)
6889         putFile16BitBE(file, content_array[i][x][y]);
6890 }
6891 #endif
6892
6893 #if ENABLE_HISTORIC_CHUNKS
6894 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6895 {
6896   int envelope_nr = element - EL_ENVELOPE_1;
6897   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6898   int chunk_size = 0;
6899   int i;
6900
6901   chunk_size += putFile16BitBE(file, element);
6902   chunk_size += putFile16BitBE(file, envelope_len);
6903   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6904   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6905
6906   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6907   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6908
6909   for (i = 0; i < envelope_len; i++)
6910     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6911
6912   return chunk_size;
6913 }
6914 #endif
6915
6916 #if ENABLE_HISTORIC_CHUNKS
6917 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6918                            int num_changed_custom_elements)
6919 {
6920   int i, check = 0;
6921
6922   putFile16BitBE(file, num_changed_custom_elements);
6923
6924   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6925   {
6926     int element = EL_CUSTOM_START + i;
6927
6928     struct ElementInfo *ei = &element_info[element];
6929
6930     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6931     {
6932       if (check < num_changed_custom_elements)
6933       {
6934         putFile16BitBE(file, element);
6935         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6936       }
6937
6938       check++;
6939     }
6940   }
6941
6942   if (check != num_changed_custom_elements)     // should not happen
6943     Error(ERR_WARN, "inconsistent number of custom element properties");
6944 }
6945 #endif
6946
6947 #if ENABLE_HISTORIC_CHUNKS
6948 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6949                            int num_changed_custom_elements)
6950 {
6951   int i, check = 0;
6952
6953   putFile16BitBE(file, num_changed_custom_elements);
6954
6955   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6956   {
6957     int element = EL_CUSTOM_START + i;
6958
6959     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6960     {
6961       if (check < num_changed_custom_elements)
6962       {
6963         putFile16BitBE(file, element);
6964         putFile16BitBE(file, element_info[element].change->target_element);
6965       }
6966
6967       check++;
6968     }
6969   }
6970
6971   if (check != num_changed_custom_elements)     // should not happen
6972     Error(ERR_WARN, "inconsistent number of custom target elements");
6973 }
6974 #endif
6975
6976 #if ENABLE_HISTORIC_CHUNKS
6977 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6978                            int num_changed_custom_elements)
6979 {
6980   int i, j, x, y, check = 0;
6981
6982   putFile16BitBE(file, num_changed_custom_elements);
6983
6984   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6985   {
6986     int element = EL_CUSTOM_START + i;
6987     struct ElementInfo *ei = &element_info[element];
6988
6989     if (ei->modified_settings)
6990     {
6991       if (check < num_changed_custom_elements)
6992       {
6993         putFile16BitBE(file, element);
6994
6995         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6996           putFile8Bit(file, ei->description[j]);
6997
6998         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6999
7000         // some free bytes for future properties and padding
7001         WriteUnusedBytesToFile(file, 7);
7002
7003         putFile8Bit(file, ei->use_gfx_element);
7004         putFile16BitBE(file, ei->gfx_element_initial);
7005
7006         putFile8Bit(file, ei->collect_score_initial);
7007         putFile8Bit(file, ei->collect_count_initial);
7008
7009         putFile16BitBE(file, ei->push_delay_fixed);
7010         putFile16BitBE(file, ei->push_delay_random);
7011         putFile16BitBE(file, ei->move_delay_fixed);
7012         putFile16BitBE(file, ei->move_delay_random);
7013
7014         putFile16BitBE(file, ei->move_pattern);
7015         putFile8Bit(file, ei->move_direction_initial);
7016         putFile8Bit(file, ei->move_stepsize);
7017
7018         for (y = 0; y < 3; y++)
7019           for (x = 0; x < 3; x++)
7020             putFile16BitBE(file, ei->content.e[x][y]);
7021
7022         putFile32BitBE(file, ei->change->events);
7023
7024         putFile16BitBE(file, ei->change->target_element);
7025
7026         putFile16BitBE(file, ei->change->delay_fixed);
7027         putFile16BitBE(file, ei->change->delay_random);
7028         putFile16BitBE(file, ei->change->delay_frames);
7029
7030         putFile16BitBE(file, ei->change->initial_trigger_element);
7031
7032         putFile8Bit(file, ei->change->explode);
7033         putFile8Bit(file, ei->change->use_target_content);
7034         putFile8Bit(file, ei->change->only_if_complete);
7035         putFile8Bit(file, ei->change->use_random_replace);
7036
7037         putFile8Bit(file, ei->change->random_percentage);
7038         putFile8Bit(file, ei->change->replace_when);
7039
7040         for (y = 0; y < 3; y++)
7041           for (x = 0; x < 3; x++)
7042             putFile16BitBE(file, ei->change->content.e[x][y]);
7043
7044         putFile8Bit(file, ei->slippery_type);
7045
7046         // some free bytes for future properties and padding
7047         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7048       }
7049
7050       check++;
7051     }
7052   }
7053
7054   if (check != num_changed_custom_elements)     // should not happen
7055     Error(ERR_WARN, "inconsistent number of custom element properties");
7056 }
7057 #endif
7058
7059 #if ENABLE_HISTORIC_CHUNKS
7060 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7061 {
7062   struct ElementInfo *ei = &element_info[element];
7063   int i, j, x, y;
7064
7065   // ---------- custom element base property values (96 bytes) ----------------
7066
7067   putFile16BitBE(file, element);
7068
7069   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7070     putFile8Bit(file, ei->description[i]);
7071
7072   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7073
7074   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
7075
7076   putFile8Bit(file, ei->num_change_pages);
7077
7078   putFile16BitBE(file, ei->ce_value_fixed_initial);
7079   putFile16BitBE(file, ei->ce_value_random_initial);
7080   putFile8Bit(file, ei->use_last_ce_value);
7081
7082   putFile8Bit(file, ei->use_gfx_element);
7083   putFile16BitBE(file, ei->gfx_element_initial);
7084
7085   putFile8Bit(file, ei->collect_score_initial);
7086   putFile8Bit(file, ei->collect_count_initial);
7087
7088   putFile8Bit(file, ei->drop_delay_fixed);
7089   putFile8Bit(file, ei->push_delay_fixed);
7090   putFile8Bit(file, ei->drop_delay_random);
7091   putFile8Bit(file, ei->push_delay_random);
7092   putFile16BitBE(file, ei->move_delay_fixed);
7093   putFile16BitBE(file, ei->move_delay_random);
7094
7095   // bits 0 - 15 of "move_pattern" ...
7096   putFile16BitBE(file, ei->move_pattern & 0xffff);
7097   putFile8Bit(file, ei->move_direction_initial);
7098   putFile8Bit(file, ei->move_stepsize);
7099
7100   putFile8Bit(file, ei->slippery_type);
7101
7102   for (y = 0; y < 3; y++)
7103     for (x = 0; x < 3; x++)
7104       putFile16BitBE(file, ei->content.e[x][y]);
7105
7106   putFile16BitBE(file, ei->move_enter_element);
7107   putFile16BitBE(file, ei->move_leave_element);
7108   putFile8Bit(file, ei->move_leave_type);
7109
7110   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7111   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7112
7113   putFile8Bit(file, ei->access_direction);
7114
7115   putFile8Bit(file, ei->explosion_delay);
7116   putFile8Bit(file, ei->ignition_delay);
7117   putFile8Bit(file, ei->explosion_type);
7118
7119   // some free bytes for future custom property values and padding
7120   WriteUnusedBytesToFile(file, 1);
7121
7122   // ---------- change page property values (48 bytes) ------------------------
7123
7124   for (i = 0; i < ei->num_change_pages; i++)
7125   {
7126     struct ElementChangeInfo *change = &ei->change_page[i];
7127     unsigned int event_bits;
7128
7129     // bits 0 - 31 of "has_event[]" ...
7130     event_bits = 0;
7131     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7132       if (change->has_event[j])
7133         event_bits |= (1 << j);
7134     putFile32BitBE(file, event_bits);
7135
7136     putFile16BitBE(file, change->target_element);
7137
7138     putFile16BitBE(file, change->delay_fixed);
7139     putFile16BitBE(file, change->delay_random);
7140     putFile16BitBE(file, change->delay_frames);
7141
7142     putFile16BitBE(file, change->initial_trigger_element);
7143
7144     putFile8Bit(file, change->explode);
7145     putFile8Bit(file, change->use_target_content);
7146     putFile8Bit(file, change->only_if_complete);
7147     putFile8Bit(file, change->use_random_replace);
7148
7149     putFile8Bit(file, change->random_percentage);
7150     putFile8Bit(file, change->replace_when);
7151
7152     for (y = 0; y < 3; y++)
7153       for (x = 0; x < 3; x++)
7154         putFile16BitBE(file, change->target_content.e[x][y]);
7155
7156     putFile8Bit(file, change->can_change);
7157
7158     putFile8Bit(file, change->trigger_side);
7159
7160     putFile8Bit(file, change->trigger_player);
7161     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7162                        log_2(change->trigger_page)));
7163
7164     putFile8Bit(file, change->has_action);
7165     putFile8Bit(file, change->action_type);
7166     putFile8Bit(file, change->action_mode);
7167     putFile16BitBE(file, change->action_arg);
7168
7169     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7170     event_bits = 0;
7171     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7172       if (change->has_event[j])
7173         event_bits |= (1 << (j - 32));
7174     putFile8Bit(file, event_bits);
7175   }
7176 }
7177 #endif
7178
7179 #if ENABLE_HISTORIC_CHUNKS
7180 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7181 {
7182   struct ElementInfo *ei = &element_info[element];
7183   struct ElementGroupInfo *group = ei->group;
7184   int i;
7185
7186   putFile16BitBE(file, element);
7187
7188   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7189     putFile8Bit(file, ei->description[i]);
7190
7191   putFile8Bit(file, group->num_elements);
7192
7193   putFile8Bit(file, ei->use_gfx_element);
7194   putFile16BitBE(file, ei->gfx_element_initial);
7195
7196   putFile8Bit(file, group->choice_mode);
7197
7198   // some free bytes for future values and padding
7199   WriteUnusedBytesToFile(file, 3);
7200
7201   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7202     putFile16BitBE(file, group->element[i]);
7203 }
7204 #endif
7205
7206 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7207                                 boolean write_element)
7208 {
7209   int save_type = entry->save_type;
7210   int data_type = entry->data_type;
7211   int conf_type = entry->conf_type;
7212   int byte_mask = conf_type & CONF_MASK_BYTES;
7213   int element = entry->element;
7214   int default_value = entry->default_value;
7215   int num_bytes = 0;
7216   boolean modified = FALSE;
7217
7218   if (byte_mask != CONF_MASK_MULTI_BYTES)
7219   {
7220     void *value_ptr = entry->value;
7221     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7222                  *(int *)value_ptr);
7223
7224     // check if any settings have been modified before saving them
7225     if (value != default_value)
7226       modified = TRUE;
7227
7228     // do not save if explicitly told or if unmodified default settings
7229     if ((save_type == SAVE_CONF_NEVER) ||
7230         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7231       return 0;
7232
7233     if (write_element)
7234       num_bytes += putFile16BitBE(file, element);
7235
7236     num_bytes += putFile8Bit(file, conf_type);
7237     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7238                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7239                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7240                   0);
7241   }
7242   else if (data_type == TYPE_STRING)
7243   {
7244     char *default_string = entry->default_string;
7245     char *string = (char *)(entry->value);
7246     int string_length = strlen(string);
7247     int i;
7248
7249     // check if any settings have been modified before saving them
7250     if (!strEqual(string, default_string))
7251       modified = TRUE;
7252
7253     // do not save if explicitly told or if unmodified default settings
7254     if ((save_type == SAVE_CONF_NEVER) ||
7255         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7256       return 0;
7257
7258     if (write_element)
7259       num_bytes += putFile16BitBE(file, element);
7260
7261     num_bytes += putFile8Bit(file, conf_type);
7262     num_bytes += putFile16BitBE(file, string_length);
7263
7264     for (i = 0; i < string_length; i++)
7265       num_bytes += putFile8Bit(file, string[i]);
7266   }
7267   else if (data_type == TYPE_ELEMENT_LIST)
7268   {
7269     int *element_array = (int *)(entry->value);
7270     int num_elements = *(int *)(entry->num_entities);
7271     int i;
7272
7273     // check if any settings have been modified before saving them
7274     for (i = 0; i < num_elements; i++)
7275       if (element_array[i] != default_value)
7276         modified = TRUE;
7277
7278     // do not save if explicitly told or if unmodified default settings
7279     if ((save_type == SAVE_CONF_NEVER) ||
7280         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7281       return 0;
7282
7283     if (write_element)
7284       num_bytes += putFile16BitBE(file, element);
7285
7286     num_bytes += putFile8Bit(file, conf_type);
7287     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7288
7289     for (i = 0; i < num_elements; i++)
7290       num_bytes += putFile16BitBE(file, element_array[i]);
7291   }
7292   else if (data_type == TYPE_CONTENT_LIST)
7293   {
7294     struct Content *content = (struct Content *)(entry->value);
7295     int num_contents = *(int *)(entry->num_entities);
7296     int i, x, y;
7297
7298     // check if any settings have been modified before saving them
7299     for (i = 0; i < num_contents; i++)
7300       for (y = 0; y < 3; y++)
7301         for (x = 0; x < 3; x++)
7302           if (content[i].e[x][y] != default_value)
7303             modified = TRUE;
7304
7305     // do not save if explicitly told or if unmodified default settings
7306     if ((save_type == SAVE_CONF_NEVER) ||
7307         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7308       return 0;
7309
7310     if (write_element)
7311       num_bytes += putFile16BitBE(file, element);
7312
7313     num_bytes += putFile8Bit(file, conf_type);
7314     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7315
7316     for (i = 0; i < num_contents; i++)
7317       for (y = 0; y < 3; y++)
7318         for (x = 0; x < 3; x++)
7319           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7320   }
7321
7322   return num_bytes;
7323 }
7324
7325 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7326 {
7327   int chunk_size = 0;
7328   int i;
7329
7330   li = *level;          // copy level data into temporary buffer
7331
7332   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7333     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7334
7335   return chunk_size;
7336 }
7337
7338 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7339 {
7340   int chunk_size = 0;
7341   int i;
7342
7343   li = *level;          // copy level data into temporary buffer
7344
7345   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7346     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7347
7348   return chunk_size;
7349 }
7350
7351 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7352 {
7353   int envelope_nr = element - EL_ENVELOPE_1;
7354   int chunk_size = 0;
7355   int i;
7356
7357   chunk_size += putFile16BitBE(file, element);
7358
7359   // copy envelope data into temporary buffer
7360   xx_envelope = level->envelope[envelope_nr];
7361
7362   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7363     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7364
7365   return chunk_size;
7366 }
7367
7368 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7369 {
7370   struct ElementInfo *ei = &element_info[element];
7371   int chunk_size = 0;
7372   int i, j;
7373
7374   chunk_size += putFile16BitBE(file, element);
7375
7376   xx_ei = *ei;          // copy element data into temporary buffer
7377
7378   // set default description string for this specific element
7379   strcpy(xx_default_description, getDefaultElementDescription(ei));
7380
7381   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7382     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7383
7384   for (i = 0; i < ei->num_change_pages; i++)
7385   {
7386     struct ElementChangeInfo *change = &ei->change_page[i];
7387
7388     xx_current_change_page = i;
7389
7390     xx_change = *change;        // copy change data into temporary buffer
7391
7392     resetEventBits();
7393     setEventBitsFromEventFlags(change);
7394
7395     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7396       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7397                                          FALSE);
7398   }
7399
7400   return chunk_size;
7401 }
7402
7403 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7404 {
7405   struct ElementInfo *ei = &element_info[element];
7406   struct ElementGroupInfo *group = ei->group;
7407   int chunk_size = 0;
7408   int i;
7409
7410   chunk_size += putFile16BitBE(file, element);
7411
7412   xx_ei = *ei;          // copy element data into temporary buffer
7413   xx_group = *group;    // copy group data into temporary buffer
7414
7415   // set default description string for this specific element
7416   strcpy(xx_default_description, getDefaultElementDescription(ei));
7417
7418   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7419     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7420
7421   return chunk_size;
7422 }
7423
7424 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7425                                   boolean save_as_template)
7426 {
7427   int chunk_size;
7428   int i;
7429   FILE *file;
7430
7431   if (!(file = fopen(filename, MODE_WRITE)))
7432   {
7433     Error(ERR_WARN, "cannot save level file '%s'", filename);
7434     return;
7435   }
7436
7437   level->file_version = FILE_VERSION_ACTUAL;
7438   level->game_version = GAME_VERSION_ACTUAL;
7439
7440   level->creation_date = getCurrentDate();
7441
7442   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7443   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7444
7445   chunk_size = SaveLevel_VERS(NULL, level);
7446   putFileChunkBE(file, "VERS", chunk_size);
7447   SaveLevel_VERS(file, level);
7448
7449   chunk_size = SaveLevel_DATE(NULL, level);
7450   putFileChunkBE(file, "DATE", chunk_size);
7451   SaveLevel_DATE(file, level);
7452
7453   chunk_size = SaveLevel_NAME(NULL, level);
7454   putFileChunkBE(file, "NAME", chunk_size);
7455   SaveLevel_NAME(file, level);
7456
7457   chunk_size = SaveLevel_AUTH(NULL, level);
7458   putFileChunkBE(file, "AUTH", chunk_size);
7459   SaveLevel_AUTH(file, level);
7460
7461   chunk_size = SaveLevel_INFO(NULL, level);
7462   putFileChunkBE(file, "INFO", chunk_size);
7463   SaveLevel_INFO(file, level);
7464
7465   chunk_size = SaveLevel_BODY(NULL, level);
7466   putFileChunkBE(file, "BODY", chunk_size);
7467   SaveLevel_BODY(file, level);
7468
7469   chunk_size = SaveLevel_ELEM(NULL, level);
7470   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
7471   {
7472     putFileChunkBE(file, "ELEM", chunk_size);
7473     SaveLevel_ELEM(file, level);
7474   }
7475
7476   for (i = 0; i < NUM_ENVELOPES; i++)
7477   {
7478     int element = EL_ENVELOPE_1 + i;
7479
7480     chunk_size = SaveLevel_NOTE(NULL, level, element);
7481     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
7482     {
7483       putFileChunkBE(file, "NOTE", chunk_size);
7484       SaveLevel_NOTE(file, level, element);
7485     }
7486   }
7487
7488   // if not using template level, check for non-default custom/group elements
7489   if (!level->use_custom_template || save_as_template)
7490   {
7491     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7492     {
7493       int element = EL_CUSTOM_START + i;
7494
7495       chunk_size = SaveLevel_CUSX(NULL, level, element);
7496       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
7497       {
7498         putFileChunkBE(file, "CUSX", chunk_size);
7499         SaveLevel_CUSX(file, level, element);
7500       }
7501     }
7502
7503     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7504     {
7505       int element = EL_GROUP_START + i;
7506
7507       chunk_size = SaveLevel_GRPX(NULL, level, element);
7508       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
7509       {
7510         putFileChunkBE(file, "GRPX", chunk_size);
7511         SaveLevel_GRPX(file, level, element);
7512       }
7513     }
7514   }
7515
7516   fclose(file);
7517
7518   SetFilePermissions(filename, PERMS_PRIVATE);
7519 }
7520
7521 void SaveLevel(int nr)
7522 {
7523   char *filename = getDefaultLevelFilename(nr);
7524
7525   SaveLevelFromFilename(&level, filename, FALSE);
7526 }
7527
7528 void SaveLevelTemplate(void)
7529 {
7530   char *filename = getLocalLevelTemplateFilename();
7531
7532   SaveLevelFromFilename(&level, filename, TRUE);
7533 }
7534
7535 boolean SaveLevelChecked(int nr)
7536 {
7537   char *filename = getDefaultLevelFilename(nr);
7538   boolean new_level = !fileExists(filename);
7539   boolean level_saved = FALSE;
7540
7541   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7542   {
7543     SaveLevel(nr);
7544
7545     if (new_level)
7546       Request("Level saved!", REQ_CONFIRM);
7547
7548     level_saved = TRUE;
7549   }
7550
7551   return level_saved;
7552 }
7553
7554 void DumpLevel(struct LevelInfo *level)
7555 {
7556   if (level->no_level_file || level->no_valid_file)
7557   {
7558     Error(ERR_WARN, "cannot dump -- no valid level file found");
7559
7560     return;
7561   }
7562
7563   PrintLine("-", 79);
7564   Print("Level xxx (file version %08d, game version %08d)\n",
7565         level->file_version, level->game_version);
7566   PrintLine("-", 79);
7567
7568   Print("Level author: '%s'\n", level->author);
7569   Print("Level title:  '%s'\n", level->name);
7570   Print("\n");
7571   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7572   Print("\n");
7573   Print("Level time:  %d seconds\n", level->time);
7574   Print("Gems needed: %d\n", level->gems_needed);
7575   Print("\n");
7576   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7577   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7578   Print("Time for light:      %d seconds\n", level->time_light);
7579   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7580   Print("\n");
7581   Print("Amoeba speed: %d\n", level->amoeba_speed);
7582   Print("\n");
7583
7584   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7585   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7586   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7587   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7588   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7589
7590   PrintLine("-", 79);
7591 }
7592
7593
7594 // ============================================================================
7595 // tape file functions
7596 // ============================================================================
7597
7598 static void setTapeInfoToDefaults(void)
7599 {
7600   int i;
7601
7602   // always start with reliable default values (empty tape)
7603   TapeErase();
7604
7605   // default values (also for pre-1.2 tapes) with only the first player
7606   tape.player_participates[0] = TRUE;
7607   for (i = 1; i < MAX_PLAYERS; i++)
7608     tape.player_participates[i] = FALSE;
7609
7610   // at least one (default: the first) player participates in every tape
7611   tape.num_participating_players = 1;
7612
7613   tape.level_nr = level_nr;
7614   tape.counter = 0;
7615   tape.changed = FALSE;
7616
7617   tape.recording = FALSE;
7618   tape.playing = FALSE;
7619   tape.pausing = FALSE;
7620
7621   tape.no_valid_file = FALSE;
7622 }
7623
7624 static int getTapePosSize(struct TapeInfo *tape)
7625 {
7626   int tape_pos_size = 0;
7627
7628   if (tape->use_key_actions)
7629     tape_pos_size += tape->num_participating_players;
7630
7631   if (tape->use_mouse_actions)
7632     tape_pos_size += 3;         // x and y position and mouse button mask
7633
7634   tape_pos_size += 1;           // tape action delay value
7635
7636   return tape_pos_size;
7637 }
7638
7639 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7640 {
7641   tape->use_key_actions = FALSE;
7642   tape->use_mouse_actions = FALSE;
7643
7644   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7645     tape->use_key_actions = TRUE;
7646
7647   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7648     tape->use_mouse_actions = TRUE;
7649 }
7650
7651 static int getTapeActionValue(struct TapeInfo *tape)
7652 {
7653   return (tape->use_key_actions &&
7654           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7655           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
7656           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7657           TAPE_ACTIONS_DEFAULT);
7658 }
7659
7660 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7661 {
7662   tape->file_version = getFileVersion(file);
7663   tape->game_version = getFileVersion(file);
7664
7665   return chunk_size;
7666 }
7667
7668 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7669 {
7670   int i;
7671
7672   tape->random_seed = getFile32BitBE(file);
7673   tape->date        = getFile32BitBE(file);
7674   tape->length      = getFile32BitBE(file);
7675
7676   // read header fields that are new since version 1.2
7677   if (tape->file_version >= FILE_VERSION_1_2)
7678   {
7679     byte store_participating_players = getFile8Bit(file);
7680     int engine_version;
7681
7682     // since version 1.2, tapes store which players participate in the tape
7683     tape->num_participating_players = 0;
7684     for (i = 0; i < MAX_PLAYERS; i++)
7685     {
7686       tape->player_participates[i] = FALSE;
7687
7688       if (store_participating_players & (1 << i))
7689       {
7690         tape->player_participates[i] = TRUE;
7691         tape->num_participating_players++;
7692       }
7693     }
7694
7695     setTapeActionFlags(tape, getFile8Bit(file));
7696
7697     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7698
7699     engine_version = getFileVersion(file);
7700     if (engine_version > 0)
7701       tape->engine_version = engine_version;
7702     else
7703       tape->engine_version = tape->game_version;
7704   }
7705
7706   return chunk_size;
7707 }
7708
7709 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7710 {
7711   int level_identifier_size;
7712   int i;
7713
7714   level_identifier_size = getFile16BitBE(file);
7715
7716   tape->level_identifier =
7717     checked_realloc(tape->level_identifier, level_identifier_size);
7718
7719   for (i = 0; i < level_identifier_size; i++)
7720     tape->level_identifier[i] = getFile8Bit(file);
7721
7722   tape->level_nr = getFile16BitBE(file);
7723
7724   chunk_size = 2 + level_identifier_size + 2;
7725
7726   return chunk_size;
7727 }
7728
7729 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7730 {
7731   int i, j;
7732   int tape_pos_size = getTapePosSize(tape);
7733   int chunk_size_expected = tape_pos_size * tape->length;
7734
7735   if (chunk_size_expected != chunk_size)
7736   {
7737     ReadUnusedBytesFromFile(file, chunk_size);
7738     return chunk_size_expected;
7739   }
7740
7741   for (i = 0; i < tape->length; i++)
7742   {
7743     if (i >= MAX_TAPE_LEN)
7744     {
7745       Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7746             MAX_TAPE_LEN);
7747
7748       // tape too large; read and ignore remaining tape data from this chunk
7749       for (;i < tape->length; i++)
7750         ReadUnusedBytesFromFile(file, tape_pos_size);
7751
7752       break;
7753     }
7754
7755     if (tape->use_key_actions)
7756     {
7757       for (j = 0; j < MAX_PLAYERS; j++)
7758       {
7759         tape->pos[i].action[j] = MV_NONE;
7760
7761         if (tape->player_participates[j])
7762           tape->pos[i].action[j] = getFile8Bit(file);
7763       }
7764     }
7765
7766     if (tape->use_mouse_actions)
7767     {
7768       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
7769       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
7770       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7771     }
7772
7773     tape->pos[i].delay = getFile8Bit(file);
7774
7775     if (tape->file_version == FILE_VERSION_1_0)
7776     {
7777       // eliminate possible diagonal moves in old tapes
7778       // this is only for backward compatibility
7779
7780       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7781       byte action = tape->pos[i].action[0];
7782       int k, num_moves = 0;
7783
7784       for (k = 0; k<4; k++)
7785       {
7786         if (action & joy_dir[k])
7787         {
7788           tape->pos[i + num_moves].action[0] = joy_dir[k];
7789           if (num_moves > 0)
7790             tape->pos[i + num_moves].delay = 0;
7791           num_moves++;
7792         }
7793       }
7794
7795       if (num_moves > 1)
7796       {
7797         num_moves--;
7798         i += num_moves;
7799         tape->length += num_moves;
7800       }
7801     }
7802     else if (tape->file_version < FILE_VERSION_2_0)
7803     {
7804       // convert pre-2.0 tapes to new tape format
7805
7806       if (tape->pos[i].delay > 1)
7807       {
7808         // action part
7809         tape->pos[i + 1] = tape->pos[i];
7810         tape->pos[i + 1].delay = 1;
7811
7812         // delay part
7813         for (j = 0; j < MAX_PLAYERS; j++)
7814           tape->pos[i].action[j] = MV_NONE;
7815         tape->pos[i].delay--;
7816
7817         i++;
7818         tape->length++;
7819       }
7820     }
7821
7822     if (checkEndOfFile(file))
7823       break;
7824   }
7825
7826   if (i != tape->length)
7827     chunk_size = tape_pos_size * i;
7828
7829   return chunk_size;
7830 }
7831
7832 static void LoadTape_SokobanSolution(char *filename)
7833 {
7834   File *file;
7835   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7836
7837   if (!(file = openFile(filename, MODE_READ)))
7838   {
7839     tape.no_valid_file = TRUE;
7840
7841     return;
7842   }
7843
7844   while (!checkEndOfFile(file))
7845   {
7846     unsigned char c = getByteFromFile(file);
7847
7848     if (checkEndOfFile(file))
7849       break;
7850
7851     switch (c)
7852     {
7853       case 'u':
7854       case 'U':
7855         tape.pos[tape.length].action[0] = MV_UP;
7856         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7857         tape.length++;
7858         break;
7859
7860       case 'd':
7861       case 'D':
7862         tape.pos[tape.length].action[0] = MV_DOWN;
7863         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7864         tape.length++;
7865         break;
7866
7867       case 'l':
7868       case 'L':
7869         tape.pos[tape.length].action[0] = MV_LEFT;
7870         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7871         tape.length++;
7872         break;
7873
7874       case 'r':
7875       case 'R':
7876         tape.pos[tape.length].action[0] = MV_RIGHT;
7877         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7878         tape.length++;
7879         break;
7880
7881       case '\n':
7882       case '\r':
7883       case '\t':
7884       case ' ':
7885         // ignore white-space characters
7886         break;
7887
7888       default:
7889         tape.no_valid_file = TRUE;
7890
7891         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7892
7893         break;
7894     }
7895   }
7896
7897   closeFile(file);
7898
7899   if (tape.no_valid_file)
7900     return;
7901
7902   tape.length_frames  = GetTapeLengthFrames();
7903   tape.length_seconds = GetTapeLengthSeconds();
7904 }
7905
7906 void LoadTapeFromFilename(char *filename)
7907 {
7908   char cookie[MAX_LINE_LEN];
7909   char chunk_name[CHUNK_ID_LEN + 1];
7910   File *file;
7911   int chunk_size;
7912
7913   // always start with reliable default values
7914   setTapeInfoToDefaults();
7915
7916   if (strSuffix(filename, ".sln"))
7917   {
7918     LoadTape_SokobanSolution(filename);
7919
7920     return;
7921   }
7922
7923   if (!(file = openFile(filename, MODE_READ)))
7924   {
7925     tape.no_valid_file = TRUE;
7926
7927     return;
7928   }
7929
7930   getFileChunkBE(file, chunk_name, NULL);
7931   if (strEqual(chunk_name, "RND1"))
7932   {
7933     getFile32BitBE(file);               // not used
7934
7935     getFileChunkBE(file, chunk_name, NULL);
7936     if (!strEqual(chunk_name, "TAPE"))
7937     {
7938       tape.no_valid_file = TRUE;
7939
7940       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7941
7942       closeFile(file);
7943
7944       return;
7945     }
7946   }
7947   else  // check for pre-2.0 file format with cookie string
7948   {
7949     strcpy(cookie, chunk_name);
7950     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7951       cookie[4] = '\0';
7952     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7953       cookie[strlen(cookie) - 1] = '\0';
7954
7955     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7956     {
7957       tape.no_valid_file = TRUE;
7958
7959       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7960
7961       closeFile(file);
7962
7963       return;
7964     }
7965
7966     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7967     {
7968       tape.no_valid_file = TRUE;
7969
7970       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7971
7972       closeFile(file);
7973
7974       return;
7975     }
7976
7977     // pre-2.0 tape files have no game version, so use file version here
7978     tape.game_version = tape.file_version;
7979   }
7980
7981   if (tape.file_version < FILE_VERSION_1_2)
7982   {
7983     // tape files from versions before 1.2.0 without chunk structure
7984     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7985     LoadTape_BODY(file, 2 * tape.length,      &tape);
7986   }
7987   else
7988   {
7989     static struct
7990     {
7991       char *name;
7992       int size;
7993       int (*loader)(File *, int, struct TapeInfo *);
7994     }
7995     chunk_info[] =
7996     {
7997       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
7998       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
7999       { "INFO", -1,                     LoadTape_INFO },
8000       { "BODY", -1,                     LoadTape_BODY },
8001       {  NULL,  0,                      NULL }
8002     };
8003
8004     while (getFileChunkBE(file, chunk_name, &chunk_size))
8005     {
8006       int i = 0;
8007
8008       while (chunk_info[i].name != NULL &&
8009              !strEqual(chunk_name, chunk_info[i].name))
8010         i++;
8011
8012       if (chunk_info[i].name == NULL)
8013       {
8014         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
8015               chunk_name, filename);
8016         ReadUnusedBytesFromFile(file, chunk_size);
8017       }
8018       else if (chunk_info[i].size != -1 &&
8019                chunk_info[i].size != chunk_size)
8020       {
8021         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8022               chunk_size, chunk_name, filename);
8023         ReadUnusedBytesFromFile(file, chunk_size);
8024       }
8025       else
8026       {
8027         // call function to load this tape chunk
8028         int chunk_size_expected =
8029           (chunk_info[i].loader)(file, chunk_size, &tape);
8030
8031         // the size of some chunks cannot be checked before reading other
8032         // chunks first (like "HEAD" and "BODY") that contain some header
8033         // information, so check them here
8034         if (chunk_size_expected != chunk_size)
8035         {
8036           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8037                 chunk_size, chunk_name, filename);
8038         }
8039       }
8040     }
8041   }
8042
8043   closeFile(file);
8044
8045   tape.length_frames  = GetTapeLengthFrames();
8046   tape.length_seconds = GetTapeLengthSeconds();
8047
8048 #if 0
8049   printf("::: tape file version: %d\n",   tape.file_version);
8050   printf("::: tape game version: %d\n",   tape.game_version);
8051   printf("::: tape engine version: %d\n", tape.engine_version);
8052 #endif
8053 }
8054
8055 void LoadTape(int nr)
8056 {
8057   char *filename = getTapeFilename(nr);
8058
8059   LoadTapeFromFilename(filename);
8060 }
8061
8062 void LoadSolutionTape(int nr)
8063 {
8064   char *filename = getSolutionTapeFilename(nr);
8065
8066   LoadTapeFromFilename(filename);
8067
8068   if (TAPE_IS_EMPTY(tape) &&
8069       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8070       level.native_sp_level->demo.is_available)
8071     CopyNativeTape_SP_to_RND(&level);
8072 }
8073
8074 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8075 {
8076   putFileVersion(file, tape->file_version);
8077   putFileVersion(file, tape->game_version);
8078 }
8079
8080 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8081 {
8082   int i;
8083   byte store_participating_players = 0;
8084
8085   // set bits for participating players for compact storage
8086   for (i = 0; i < MAX_PLAYERS; i++)
8087     if (tape->player_participates[i])
8088       store_participating_players |= (1 << i);
8089
8090   putFile32BitBE(file, tape->random_seed);
8091   putFile32BitBE(file, tape->date);
8092   putFile32BitBE(file, tape->length);
8093
8094   putFile8Bit(file, store_participating_players);
8095
8096   putFile8Bit(file, getTapeActionValue(tape));
8097
8098   // unused bytes not at the end here for 4-byte alignment of engine_version
8099   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8100
8101   putFileVersion(file, tape->engine_version);
8102 }
8103
8104 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8105 {
8106   int level_identifier_size = strlen(tape->level_identifier) + 1;
8107   int i;
8108
8109   putFile16BitBE(file, level_identifier_size);
8110
8111   for (i = 0; i < level_identifier_size; i++)
8112     putFile8Bit(file, tape->level_identifier[i]);
8113
8114   putFile16BitBE(file, tape->level_nr);
8115 }
8116
8117 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8118 {
8119   int i, j;
8120
8121   for (i = 0; i < tape->length; i++)
8122   {
8123     if (tape->use_key_actions)
8124     {
8125       for (j = 0; j < MAX_PLAYERS; j++)
8126         if (tape->player_participates[j])
8127           putFile8Bit(file, tape->pos[i].action[j]);
8128     }
8129
8130     if (tape->use_mouse_actions)
8131     {
8132       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8133       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8134       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8135     }
8136
8137     putFile8Bit(file, tape->pos[i].delay);
8138   }
8139 }
8140
8141 void SaveTape(int nr)
8142 {
8143   char *filename = getTapeFilename(nr);
8144   FILE *file;
8145   int tape_pos_size;
8146   int info_chunk_size;
8147   int body_chunk_size;
8148   int i;
8149
8150   InitTapeDirectory(leveldir_current->subdir);
8151
8152   if (!(file = fopen(filename, MODE_WRITE)))
8153   {
8154     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8155     return;
8156   }
8157
8158   tape.file_version = FILE_VERSION_ACTUAL;
8159   tape.game_version = GAME_VERSION_ACTUAL;
8160
8161   tape.num_participating_players = 0;
8162
8163   // count number of participating players
8164   for (i = 0; i < MAX_PLAYERS; i++)
8165     if (tape.player_participates[i])
8166       tape.num_participating_players++;
8167
8168   tape_pos_size = getTapePosSize(&tape);
8169
8170   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8171   body_chunk_size = tape_pos_size * tape.length;
8172
8173   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8174   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8175
8176   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8177   SaveTape_VERS(file, &tape);
8178
8179   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8180   SaveTape_HEAD(file, &tape);
8181
8182   putFileChunkBE(file, "INFO", info_chunk_size);
8183   SaveTape_INFO(file, &tape);
8184
8185   putFileChunkBE(file, "BODY", body_chunk_size);
8186   SaveTape_BODY(file, &tape);
8187
8188   fclose(file);
8189
8190   SetFilePermissions(filename, PERMS_PRIVATE);
8191
8192   tape.changed = FALSE;
8193 }
8194
8195 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8196                                   unsigned int req_state_added)
8197 {
8198   char *filename = getTapeFilename(nr);
8199   boolean new_tape = !fileExists(filename);
8200   boolean tape_saved = FALSE;
8201
8202   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8203   {
8204     SaveTape(nr);
8205
8206     if (new_tape)
8207       Request(msg_saved, REQ_CONFIRM | req_state_added);
8208
8209     tape_saved = TRUE;
8210   }
8211
8212   return tape_saved;
8213 }
8214
8215 boolean SaveTapeChecked(int nr)
8216 {
8217   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8218 }
8219
8220 boolean SaveTapeChecked_LevelSolved(int nr)
8221 {
8222   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8223                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
8224 }
8225
8226 void DumpTape(struct TapeInfo *tape)
8227 {
8228   int tape_frame_counter;
8229   int i, j;
8230
8231   if (tape->no_valid_file)
8232   {
8233     Error(ERR_WARN, "cannot dump -- no valid tape file found");
8234
8235     return;
8236   }
8237
8238   PrintLine("-", 79);
8239   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8240         tape->level_nr, tape->file_version, tape->game_version);
8241   Print("                  (effective engine version %08d)\n",
8242         tape->engine_version);
8243   Print("Level series identifier: '%s'\n", tape->level_identifier);
8244   PrintLine("-", 79);
8245
8246   tape_frame_counter = 0;
8247
8248   for (i = 0; i < tape->length; i++)
8249   {
8250     if (i >= MAX_TAPE_LEN)
8251       break;
8252
8253     Print("%04d: ", i);
8254
8255     for (j = 0; j < MAX_PLAYERS; j++)
8256     {
8257       if (tape->player_participates[j])
8258       {
8259         int action = tape->pos[i].action[j];
8260
8261         Print("%d:%02x ", j, action);
8262         Print("[%c%c%c%c|%c%c] - ",
8263               (action & JOY_LEFT ? '<' : ' '),
8264               (action & JOY_RIGHT ? '>' : ' '),
8265               (action & JOY_UP ? '^' : ' '),
8266               (action & JOY_DOWN ? 'v' : ' '),
8267               (action & JOY_BUTTON_1 ? '1' : ' '),
8268               (action & JOY_BUTTON_2 ? '2' : ' '));
8269       }
8270     }
8271
8272     Print("(%03d) ", tape->pos[i].delay);
8273     Print("[%05d]\n", tape_frame_counter);
8274
8275     tape_frame_counter += tape->pos[i].delay;
8276   }
8277
8278   PrintLine("-", 79);
8279 }
8280
8281
8282 // ============================================================================
8283 // score file functions
8284 // ============================================================================
8285
8286 void LoadScore(int nr)
8287 {
8288   int i;
8289   char *filename = getScoreFilename(nr);
8290   char cookie[MAX_LINE_LEN];
8291   char line[MAX_LINE_LEN];
8292   char *line_ptr;
8293   FILE *file;
8294
8295   // always start with reliable default values
8296   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8297   {
8298     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8299     highscore[i].Score = 0;
8300   }
8301
8302   if (!(file = fopen(filename, MODE_READ)))
8303     return;
8304
8305   // check file identifier
8306   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8307     cookie[0] = '\0';
8308   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8309     cookie[strlen(cookie) - 1] = '\0';
8310
8311   if (!checkCookieString(cookie, SCORE_COOKIE))
8312   {
8313     Error(ERR_WARN, "unknown format of score file '%s'", filename);
8314     fclose(file);
8315     return;
8316   }
8317
8318   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8319   {
8320     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8321       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8322     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8323       line[0] = '\0';
8324
8325     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8326       line[strlen(line) - 1] = '\0';
8327
8328     for (line_ptr = line; *line_ptr; line_ptr++)
8329     {
8330       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8331       {
8332         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8333         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8334         break;
8335       }
8336     }
8337   }
8338
8339   fclose(file);
8340 }
8341
8342 void SaveScore(int nr)
8343 {
8344   int i;
8345   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8346   char *filename = getScoreFilename(nr);
8347   FILE *file;
8348
8349   // used instead of "leveldir_current->subdir" (for network games)
8350   InitScoreDirectory(levelset.identifier);
8351
8352   if (!(file = fopen(filename, MODE_WRITE)))
8353   {
8354     Error(ERR_WARN, "cannot save score for level %d", nr);
8355     return;
8356   }
8357
8358   fprintf(file, "%s\n\n", SCORE_COOKIE);
8359
8360   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8361     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8362
8363   fclose(file);
8364
8365   SetFilePermissions(filename, permissions);
8366 }
8367
8368
8369 // ============================================================================
8370 // setup file functions
8371 // ============================================================================
8372
8373 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8374
8375
8376 static struct TokenInfo global_setup_tokens[] =
8377 {
8378   {
8379     TYPE_STRING,
8380     &setup.player_name,                         "player_name"
8381   },
8382   {
8383     TYPE_SWITCH,
8384     &setup.sound,                               "sound"
8385   },
8386   {
8387     TYPE_SWITCH,
8388     &setup.sound_loops,                         "repeating_sound_loops"
8389   },
8390   {
8391     TYPE_SWITCH,
8392     &setup.sound_music,                         "background_music"
8393   },
8394   {
8395     TYPE_SWITCH,
8396     &setup.sound_simple,                        "simple_sound_effects"
8397   },
8398   {
8399     TYPE_SWITCH,
8400     &setup.toons,                               "toons"
8401   },
8402   {
8403     TYPE_SWITCH,
8404     &setup.scroll_delay,                        "scroll_delay"
8405   },
8406   {
8407     TYPE_SWITCH,
8408     &setup.forced_scroll_delay,                 "forced_scroll_delay"
8409   },
8410   {
8411     TYPE_INTEGER,
8412     &setup.scroll_delay_value,                  "scroll_delay_value"
8413   },
8414   {
8415     TYPE_STRING,
8416     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
8417   },
8418   {
8419     TYPE_INTEGER,
8420     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
8421   },
8422   {
8423     TYPE_SWITCH,
8424     &setup.fade_screens,                        "fade_screens"
8425   },
8426   {
8427     TYPE_SWITCH,
8428     &setup.autorecord,                          "automatic_tape_recording"
8429   },
8430   {
8431     TYPE_SWITCH,
8432     &setup.show_titlescreen,                    "show_titlescreen"
8433   },
8434   {
8435     TYPE_SWITCH,
8436     &setup.quick_doors,                         "quick_doors"
8437   },
8438   {
8439     TYPE_SWITCH,
8440     &setup.team_mode,                           "team_mode"
8441   },
8442   {
8443     TYPE_SWITCH,
8444     &setup.handicap,                            "handicap"
8445   },
8446   {
8447     TYPE_SWITCH,
8448     &setup.skip_levels,                         "skip_levels"
8449   },
8450   {
8451     TYPE_SWITCH,
8452     &setup.increment_levels,                    "increment_levels"
8453   },
8454   {
8455     TYPE_SWITCH,
8456     &setup.auto_play_next_level,                "auto_play_next_level"
8457   },
8458   {
8459     TYPE_SWITCH,
8460     &setup.skip_scores_after_game,              "skip_scores_after_game"
8461   },
8462   {
8463     TYPE_SWITCH,
8464     &setup.time_limit,                          "time_limit"
8465   },
8466   {
8467     TYPE_SWITCH,
8468     &setup.fullscreen,                          "fullscreen"
8469   },
8470   {
8471     TYPE_INTEGER,
8472     &setup.window_scaling_percent,              "window_scaling_percent"
8473   },
8474   {
8475     TYPE_STRING,
8476     &setup.window_scaling_quality,              "window_scaling_quality"
8477   },
8478   {
8479     TYPE_STRING,
8480     &setup.screen_rendering_mode,               "screen_rendering_mode"
8481   },
8482   {
8483     TYPE_STRING,
8484     &setup.vsync_mode,                          "vsync_mode"
8485   },
8486   {
8487     TYPE_SWITCH,
8488     &setup.ask_on_escape,                       "ask_on_escape"
8489   },
8490   {
8491     TYPE_SWITCH,
8492     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
8493   },
8494   {
8495     TYPE_SWITCH,
8496     &setup.ask_on_game_over,                    "ask_on_game_over"
8497   },
8498   {
8499     TYPE_SWITCH,
8500     &setup.quick_switch,                        "quick_player_switch"
8501   },
8502   {
8503     TYPE_SWITCH,
8504     &setup.input_on_focus,                      "input_on_focus"
8505   },
8506   {
8507     TYPE_SWITCH,
8508     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
8509   },
8510   {
8511     TYPE_SWITCH,
8512     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
8513   },
8514   {
8515     TYPE_SWITCH,
8516     &setup.game_speed_extended,                 "game_speed_extended"
8517   },
8518   {
8519     TYPE_INTEGER,
8520     &setup.game_frame_delay,                    "game_frame_delay"
8521   },
8522   {
8523     TYPE_SWITCH,
8524     &setup.sp_show_border_elements,             "sp_show_border_elements"
8525   },
8526   {
8527     TYPE_SWITCH,
8528     &setup.small_game_graphics,                 "small_game_graphics"
8529   },
8530   {
8531     TYPE_SWITCH,
8532     &setup.show_snapshot_buttons,               "show_snapshot_buttons"
8533   },
8534   {
8535     TYPE_STRING,
8536     &setup.graphics_set,                        "graphics_set"
8537   },
8538   {
8539     TYPE_STRING,
8540     &setup.sounds_set,                          "sounds_set"
8541   },
8542   {
8543     TYPE_STRING,
8544     &setup.music_set,                           "music_set"
8545   },
8546   {
8547     TYPE_SWITCH3,
8548     &setup.override_level_graphics,             "override_level_graphics"
8549   },
8550   {
8551     TYPE_SWITCH3,
8552     &setup.override_level_sounds,               "override_level_sounds"
8553   },
8554   {
8555     TYPE_SWITCH3,
8556     &setup.override_level_music,                "override_level_music"
8557   },
8558   {
8559     TYPE_INTEGER,
8560     &setup.volume_simple,                       "volume_simple"
8561   },
8562   {
8563     TYPE_INTEGER,
8564     &setup.volume_loops,                        "volume_loops"
8565   },
8566   {
8567     TYPE_INTEGER,
8568     &setup.volume_music,                        "volume_music"
8569   },
8570   {
8571     TYPE_SWITCH,
8572     &setup.network_mode,                        "network_mode"
8573   },
8574   {
8575     TYPE_PLAYER,
8576     &setup.network_player_nr,                   "network_player"
8577   },
8578   {
8579     TYPE_STRING,
8580     &setup.network_server_hostname,             "network_server_hostname"
8581   },
8582   {
8583     TYPE_STRING,
8584     &setup.touch.control_type,                  "touch.control_type"
8585   },
8586   {
8587     TYPE_INTEGER,
8588     &setup.touch.move_distance,                 "touch.move_distance"
8589   },
8590   {
8591     TYPE_INTEGER,
8592     &setup.touch.drop_distance,                 "touch.drop_distance"
8593   },
8594   {
8595     TYPE_INTEGER,
8596     &setup.touch.transparency,                  "touch.transparency"
8597   },
8598   {
8599     TYPE_INTEGER,
8600     &setup.touch.draw_outlined,                 "touch.draw_outlined"
8601   },
8602   {
8603     TYPE_INTEGER,
8604     &setup.touch.draw_pressed,                  "touch.draw_pressed"
8605   },
8606   {
8607     TYPE_INTEGER,
8608     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
8609   },
8610   {
8611     TYPE_INTEGER,
8612     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
8613   },
8614   {
8615     TYPE_INTEGER,
8616     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
8617   },
8618   {
8619     TYPE_INTEGER,
8620     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
8621   },
8622 };
8623
8624 static struct TokenInfo auto_setup_tokens[] =
8625 {
8626   {
8627     TYPE_INTEGER,
8628     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
8629   },
8630 };
8631
8632 static struct TokenInfo editor_setup_tokens[] =
8633 {
8634   {
8635     TYPE_SWITCH,
8636     &setup.editor.el_classic,                   "editor.el_classic"
8637   },
8638   {
8639     TYPE_SWITCH,
8640     &setup.editor.el_custom,                    "editor.el_custom"
8641   },
8642   {
8643     TYPE_SWITCH,
8644     &setup.editor.el_user_defined,              "editor.el_user_defined"
8645   },
8646   {
8647     TYPE_SWITCH,
8648     &setup.editor.el_dynamic,                   "editor.el_dynamic"
8649   },
8650   {
8651     TYPE_SWITCH,
8652     &setup.editor.el_headlines,                 "editor.el_headlines"
8653   },
8654   {
8655     TYPE_SWITCH,
8656     &setup.editor.show_element_token,           "editor.show_element_token"
8657   },
8658 };
8659
8660 static struct TokenInfo editor_cascade_setup_tokens[] =
8661 {
8662   {
8663     TYPE_SWITCH,
8664     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
8665   },
8666   {
8667     TYPE_SWITCH,
8668     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
8669   },
8670   {
8671     TYPE_SWITCH,
8672     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
8673   },
8674   {
8675     TYPE_SWITCH,
8676     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
8677   },
8678   {
8679     TYPE_SWITCH,
8680     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
8681   },
8682   {
8683     TYPE_SWITCH,
8684     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
8685   },
8686   {
8687     TYPE_SWITCH,
8688     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
8689   },
8690   {
8691     TYPE_SWITCH,
8692     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
8693   },
8694   {
8695     TYPE_SWITCH,
8696     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
8697   },
8698   {
8699     TYPE_SWITCH,
8700     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
8701   },
8702   {
8703     TYPE_SWITCH,
8704     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
8705   },
8706   {
8707     TYPE_SWITCH,
8708     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
8709   },
8710   {
8711     TYPE_SWITCH,
8712     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
8713   },
8714   {
8715     TYPE_SWITCH,
8716     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
8717   },
8718   {
8719     TYPE_SWITCH,
8720     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
8721   },
8722   {
8723     TYPE_SWITCH,
8724     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
8725   },
8726   {
8727     TYPE_SWITCH,
8728     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
8729   },
8730 };
8731
8732 static struct TokenInfo shortcut_setup_tokens[] =
8733 {
8734   {
8735     TYPE_KEY_X11,
8736     &setup.shortcut.save_game,                  "shortcut.save_game"
8737   },
8738   {
8739     TYPE_KEY_X11,
8740     &setup.shortcut.load_game,                  "shortcut.load_game"
8741   },
8742   {
8743     TYPE_KEY_X11,
8744     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
8745   },
8746   {
8747     TYPE_KEY_X11,
8748     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
8749   },
8750   {
8751     TYPE_KEY_X11,
8752     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
8753   },
8754   {
8755     TYPE_KEY_X11,
8756     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
8757   },
8758   {
8759     TYPE_KEY_X11,
8760     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
8761   },
8762   {
8763     TYPE_KEY_X11,
8764     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
8765   },
8766   {
8767     TYPE_KEY_X11,
8768     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
8769   },
8770   {
8771     TYPE_KEY_X11,
8772     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
8773   },
8774   {
8775     TYPE_KEY_X11,
8776     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
8777   },
8778   {
8779     TYPE_KEY_X11,
8780     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
8781   },
8782   {
8783     TYPE_KEY_X11,
8784     &setup.shortcut.tape_record,                "shortcut.tape_record"
8785   },
8786   {
8787     TYPE_KEY_X11,
8788     &setup.shortcut.tape_play,                  "shortcut.tape_play"
8789   },
8790   {
8791     TYPE_KEY_X11,
8792     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
8793   },
8794   {
8795     TYPE_KEY_X11,
8796     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
8797   },
8798   {
8799     TYPE_KEY_X11,
8800     &setup.shortcut.sound_music,                "shortcut.sound_music"
8801   },
8802   {
8803     TYPE_KEY_X11,
8804     &setup.shortcut.snap_left,                  "shortcut.snap_left"
8805   },
8806   {
8807     TYPE_KEY_X11,
8808     &setup.shortcut.snap_right,                 "shortcut.snap_right"
8809   },
8810   {
8811     TYPE_KEY_X11,
8812     &setup.shortcut.snap_up,                    "shortcut.snap_up"
8813   },
8814   {
8815     TYPE_KEY_X11,
8816     &setup.shortcut.snap_down,                  "shortcut.snap_down"
8817   },
8818 };
8819
8820 static struct SetupInputInfo setup_input;
8821 static struct TokenInfo player_setup_tokens[] =
8822 {
8823   {
8824     TYPE_BOOLEAN,
8825     &setup_input.use_joystick,                  ".use_joystick"
8826   },
8827   {
8828     TYPE_STRING,
8829     &setup_input.joy.device_name,               ".joy.device_name"
8830   },
8831   {
8832     TYPE_INTEGER,
8833     &setup_input.joy.xleft,                     ".joy.xleft"
8834   },
8835   {
8836     TYPE_INTEGER,
8837     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
8838   },
8839   {
8840     TYPE_INTEGER,
8841     &setup_input.joy.xright,                    ".joy.xright"
8842   },
8843   {
8844     TYPE_INTEGER,
8845     &setup_input.joy.yupper,                    ".joy.yupper"
8846   },
8847   {
8848     TYPE_INTEGER,
8849     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
8850   },
8851   {
8852     TYPE_INTEGER,
8853     &setup_input.joy.ylower,                    ".joy.ylower"
8854   },
8855   {
8856     TYPE_INTEGER,
8857     &setup_input.joy.snap,                      ".joy.snap_field"
8858   },
8859   {
8860     TYPE_INTEGER,
8861     &setup_input.joy.drop,                      ".joy.place_bomb"
8862   },
8863   {
8864     TYPE_KEY_X11,
8865     &setup_input.key.left,                      ".key.move_left"
8866   },
8867   {
8868     TYPE_KEY_X11,
8869     &setup_input.key.right,                     ".key.move_right"
8870   },
8871   {
8872     TYPE_KEY_X11,
8873     &setup_input.key.up,                        ".key.move_up"
8874   },
8875   {
8876     TYPE_KEY_X11,
8877     &setup_input.key.down,                      ".key.move_down"
8878   },
8879   {
8880     TYPE_KEY_X11,
8881     &setup_input.key.snap,                      ".key.snap_field"
8882   },
8883   {
8884     TYPE_KEY_X11,
8885     &setup_input.key.drop,                      ".key.place_bomb"
8886   },
8887 };
8888
8889 static struct TokenInfo system_setup_tokens[] =
8890 {
8891   {
8892     TYPE_STRING,
8893     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
8894   },
8895   {
8896     TYPE_STRING,
8897     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
8898   },
8899   {
8900     TYPE_INTEGER,
8901     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
8902   },
8903 };
8904
8905 static struct TokenInfo internal_setup_tokens[] =
8906 {
8907   {
8908     TYPE_STRING,
8909     &setup.internal.program_title,              "program_title"
8910   },
8911   {
8912     TYPE_STRING,
8913     &setup.internal.program_version,            "program_version"
8914   },
8915   {
8916     TYPE_STRING,
8917     &setup.internal.program_author,             "program_author"
8918   },
8919   {
8920     TYPE_STRING,
8921     &setup.internal.program_email,              "program_email"
8922   },
8923   {
8924     TYPE_STRING,
8925     &setup.internal.program_website,            "program_website"
8926   },
8927   {
8928     TYPE_STRING,
8929     &setup.internal.program_copyright,          "program_copyright"
8930   },
8931   {
8932     TYPE_STRING,
8933     &setup.internal.program_company,            "program_company"
8934   },
8935   {
8936     TYPE_STRING,
8937     &setup.internal.program_icon_file,          "program_icon_file"
8938   },
8939   {
8940     TYPE_STRING,
8941     &setup.internal.default_graphics_set,       "default_graphics_set"
8942   },
8943   {
8944     TYPE_STRING,
8945     &setup.internal.default_sounds_set,         "default_sounds_set"
8946   },
8947   {
8948     TYPE_STRING,
8949     &setup.internal.default_music_set,          "default_music_set"
8950   },
8951   {
8952     TYPE_STRING,
8953     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
8954   },
8955   {
8956     TYPE_STRING,
8957     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
8958   },
8959   {
8960     TYPE_STRING,
8961     &setup.internal.fallback_music_file,        "fallback_music_file"
8962   },
8963   {
8964     TYPE_STRING,
8965     &setup.internal.default_level_series,       "default_level_series"
8966   },
8967   {
8968     TYPE_INTEGER,
8969     &setup.internal.default_window_width,       "default_window_width"
8970   },
8971   {
8972     TYPE_INTEGER,
8973     &setup.internal.default_window_height,      "default_window_height"
8974   },
8975   {
8976     TYPE_BOOLEAN,
8977     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
8978   },
8979   {
8980     TYPE_BOOLEAN,
8981     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
8982   },
8983   {
8984     TYPE_BOOLEAN,
8985     &setup.internal.create_user_levelset,       "create_user_levelset"
8986   },
8987   {
8988     TYPE_BOOLEAN,
8989     &setup.internal.menu_game,                  "menu_game"
8990   },
8991   {
8992     TYPE_BOOLEAN,
8993     &setup.internal.menu_editor,                "menu_editor"
8994   },
8995   {
8996     TYPE_BOOLEAN,
8997     &setup.internal.menu_graphics,              "menu_graphics"
8998   },
8999   {
9000     TYPE_BOOLEAN,
9001     &setup.internal.menu_sound,                 "menu_sound"
9002   },
9003   {
9004     TYPE_BOOLEAN,
9005     &setup.internal.menu_artwork,               "menu_artwork"
9006   },
9007   {
9008     TYPE_BOOLEAN,
9009     &setup.internal.menu_input,                 "menu_input"
9010   },
9011   {
9012     TYPE_BOOLEAN,
9013     &setup.internal.menu_touch,                 "menu_touch"
9014   },
9015   {
9016     TYPE_BOOLEAN,
9017     &setup.internal.menu_shortcuts,             "menu_shortcuts"
9018   },
9019   {
9020     TYPE_BOOLEAN,
9021     &setup.internal.menu_exit,                  "menu_exit"
9022   },
9023   {
9024     TYPE_BOOLEAN,
9025     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
9026   },
9027 };
9028
9029 static struct TokenInfo debug_setup_tokens[] =
9030 {
9031   {
9032     TYPE_INTEGER,
9033     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
9034   },
9035   {
9036     TYPE_INTEGER,
9037     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
9038   },
9039   {
9040     TYPE_INTEGER,
9041     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
9042   },
9043   {
9044     TYPE_INTEGER,
9045     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
9046   },
9047   {
9048     TYPE_INTEGER,
9049     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
9050   },
9051   {
9052     TYPE_INTEGER,
9053     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
9054   },
9055   {
9056     TYPE_INTEGER,
9057     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
9058   },
9059   {
9060     TYPE_INTEGER,
9061     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
9062   },
9063   {
9064     TYPE_INTEGER,
9065     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
9066   },
9067   {
9068     TYPE_INTEGER,
9069     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
9070   },
9071   {
9072     TYPE_KEY_X11,
9073     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
9074   },
9075   {
9076     TYPE_KEY_X11,
9077     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
9078   },
9079   {
9080     TYPE_KEY_X11,
9081     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
9082   },
9083   {
9084     TYPE_KEY_X11,
9085     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
9086   },
9087   {
9088     TYPE_KEY_X11,
9089     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
9090   },
9091   {
9092     TYPE_KEY_X11,
9093     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
9094   },
9095   {
9096     TYPE_KEY_X11,
9097     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
9098   },
9099   {
9100     TYPE_KEY_X11,
9101     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
9102   },
9103   {
9104     TYPE_KEY_X11,
9105     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
9106   },
9107   {
9108     TYPE_KEY_X11,
9109     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
9110   },
9111   {
9112     TYPE_BOOLEAN,
9113     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
9114   {
9115     TYPE_BOOLEAN,
9116     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
9117   },
9118   {
9119     TYPE_BOOLEAN,
9120     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
9121   },
9122 };
9123
9124 static struct TokenInfo options_setup_tokens[] =
9125 {
9126   {
9127     TYPE_BOOLEAN,
9128     &setup.options.verbose,                     "options.verbose"
9129   },
9130 };
9131
9132 static char *get_corrected_login_name(char *login_name)
9133 {
9134   // needed because player name must be a fixed length string
9135   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
9136
9137   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
9138   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
9139
9140   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         // name has been cut
9141     if (strchr(login_name_new, ' '))
9142       *strchr(login_name_new, ' ') = '\0';
9143
9144   return login_name_new;
9145 }
9146
9147 static void setSetupInfoToDefaults(struct SetupInfo *si)
9148 {
9149   int i;
9150
9151   si->player_name = get_corrected_login_name(getLoginName());
9152
9153   si->sound = TRUE;
9154   si->sound_loops = TRUE;
9155   si->sound_music = TRUE;
9156   si->sound_simple = TRUE;
9157   si->toons = TRUE;
9158   si->scroll_delay = TRUE;
9159   si->forced_scroll_delay = FALSE;
9160   si->scroll_delay_value = STD_SCROLL_DELAY;
9161   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9162   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9163   si->fade_screens = TRUE;
9164   si->autorecord = TRUE;
9165   si->show_titlescreen = TRUE;
9166   si->quick_doors = FALSE;
9167   si->team_mode = FALSE;
9168   si->handicap = TRUE;
9169   si->skip_levels = TRUE;
9170   si->increment_levels = TRUE;
9171   si->auto_play_next_level = TRUE;
9172   si->skip_scores_after_game = FALSE;
9173   si->time_limit = TRUE;
9174   si->fullscreen = FALSE;
9175   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9176   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9177   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9178   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9179   si->ask_on_escape = TRUE;
9180   si->ask_on_escape_editor = TRUE;
9181   si->ask_on_game_over = TRUE;
9182   si->quick_switch = FALSE;
9183   si->input_on_focus = FALSE;
9184   si->prefer_aga_graphics = TRUE;
9185   si->prefer_lowpass_sounds = FALSE;
9186   si->game_speed_extended = FALSE;
9187   si->game_frame_delay = GAME_FRAME_DELAY;
9188   si->sp_show_border_elements = FALSE;
9189   si->small_game_graphics = FALSE;
9190   si->show_snapshot_buttons = FALSE;
9191
9192   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9193   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
9194   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
9195
9196   si->override_level_graphics = FALSE;
9197   si->override_level_sounds = FALSE;
9198   si->override_level_music = FALSE;
9199
9200   si->volume_simple = 100;              // percent
9201   si->volume_loops = 100;               // percent
9202   si->volume_music = 100;               // percent
9203
9204   si->network_mode = FALSE;
9205   si->network_player_nr = 0;            // first player
9206   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9207
9208   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9209   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
9210   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
9211   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
9212   si->touch.draw_outlined = TRUE;
9213   si->touch.draw_pressed = TRUE;
9214
9215   for (i = 0; i < 2; i++)
9216   {
9217     char *default_grid_button[6][2] =
9218     {
9219       { "      ", "  ^^  " },
9220       { "      ", "  ^^  " },
9221       { "      ", "<<  >>" },
9222       { "      ", "<<  >>" },
9223       { "111222", "  vv  " },
9224       { "111222", "  vv  " }
9225     };
9226     int grid_xsize = DEFAULT_GRID_XSIZE(i);
9227     int grid_ysize = DEFAULT_GRID_YSIZE(i);
9228     int min_xsize = MIN(6, grid_xsize);
9229     int min_ysize = MIN(6, grid_ysize);
9230     int startx = grid_xsize - min_xsize;
9231     int starty = grid_ysize - min_ysize;
9232     int x, y;
9233
9234     // virtual buttons grid can only be set to defaults if video is initialized
9235     // (this will be repeated if virtual buttons are not loaded from setup file)
9236     if (video.initialized)
9237     {
9238       si->touch.grid_xsize[i] = grid_xsize;
9239       si->touch.grid_ysize[i] = grid_ysize;
9240     }
9241     else
9242     {
9243       si->touch.grid_xsize[i] = -1;
9244       si->touch.grid_ysize[i] = -1;
9245     }
9246
9247     for (x = 0; x < MAX_GRID_XSIZE; x++)
9248       for (y = 0; y < MAX_GRID_YSIZE; y++)
9249         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9250
9251     for (x = 0; x < min_xsize; x++)
9252       for (y = 0; y < min_ysize; y++)
9253         si->touch.grid_button[i][x][starty + y] =
9254           default_grid_button[y][0][x];
9255
9256     for (x = 0; x < min_xsize; x++)
9257       for (y = 0; y < min_ysize; y++)
9258         si->touch.grid_button[i][startx + x][starty + y] =
9259           default_grid_button[y][1][x];
9260   }
9261
9262   si->touch.grid_initialized            = video.initialized;
9263
9264   si->editor.el_boulderdash             = TRUE;
9265   si->editor.el_emerald_mine            = TRUE;
9266   si->editor.el_emerald_mine_club       = TRUE;
9267   si->editor.el_more                    = TRUE;
9268   si->editor.el_sokoban                 = TRUE;
9269   si->editor.el_supaplex                = TRUE;
9270   si->editor.el_diamond_caves           = TRUE;
9271   si->editor.el_dx_boulderdash          = TRUE;
9272
9273   si->editor.el_mirror_magic            = TRUE;
9274   si->editor.el_deflektor               = TRUE;
9275
9276   si->editor.el_chars                   = TRUE;
9277   si->editor.el_steel_chars             = TRUE;
9278
9279   si->editor.el_classic                 = TRUE;
9280   si->editor.el_custom                  = TRUE;
9281
9282   si->editor.el_user_defined            = FALSE;
9283   si->editor.el_dynamic                 = TRUE;
9284
9285   si->editor.el_headlines               = TRUE;
9286
9287   si->editor.show_element_token         = FALSE;
9288
9289   si->editor.use_template_for_new_levels = TRUE;
9290
9291   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
9292   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
9293   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
9294
9295   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
9296   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
9297   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
9298   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
9299   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9300
9301   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
9302   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
9303   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
9304   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
9305   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
9306   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
9307
9308   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
9309   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
9310   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
9311
9312   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
9313   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
9314   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
9315   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
9316
9317   for (i = 0; i < MAX_PLAYERS; i++)
9318   {
9319     si->input[i].use_joystick = FALSE;
9320     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9321     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
9322     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9323     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
9324     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
9325     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9326     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
9327     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
9328     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
9329     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
9330     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9331     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
9332     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
9333     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
9334     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
9335   }
9336
9337   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9338   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9339   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9340
9341   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
9342   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
9343   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
9344   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
9345   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
9346   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9347   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
9348
9349   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9350
9351   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9352   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
9353   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
9354
9355   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9356   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
9357   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
9358
9359   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9360   si->internal.choose_from_top_leveldir = FALSE;
9361   si->internal.show_scaling_in_title = TRUE;
9362   si->internal.create_user_levelset = TRUE;
9363
9364   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
9365   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9366
9367   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9368   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9369   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9370   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9371   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9372   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9373   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9374   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9375   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9376   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9377
9378   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9379   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9380   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9381   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9382   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9383   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9384   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9385   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9386   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9387   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9388
9389   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9390   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
9391
9392   si->debug.show_frames_per_second = FALSE;
9393
9394   si->options.verbose = FALSE;
9395
9396 #if defined(PLATFORM_ANDROID)
9397   si->fullscreen = TRUE;
9398 #endif
9399 }
9400
9401 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9402 {
9403   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9404 }
9405
9406 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9407 {
9408   si->editor_cascade.el_bd              = TRUE;
9409   si->editor_cascade.el_em              = TRUE;
9410   si->editor_cascade.el_emc             = TRUE;
9411   si->editor_cascade.el_rnd             = TRUE;
9412   si->editor_cascade.el_sb              = TRUE;
9413   si->editor_cascade.el_sp              = TRUE;
9414   si->editor_cascade.el_dc              = TRUE;
9415   si->editor_cascade.el_dx              = TRUE;
9416
9417   si->editor_cascade.el_mm              = TRUE;
9418   si->editor_cascade.el_df              = TRUE;
9419
9420   si->editor_cascade.el_chars           = FALSE;
9421   si->editor_cascade.el_steel_chars     = FALSE;
9422   si->editor_cascade.el_ce              = FALSE;
9423   si->editor_cascade.el_ge              = FALSE;
9424   si->editor_cascade.el_ref             = FALSE;
9425   si->editor_cascade.el_user            = FALSE;
9426   si->editor_cascade.el_dynamic         = FALSE;
9427 }
9428
9429 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
9430
9431 static char *getHideSetupToken(void *setup_value)
9432 {
9433   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9434
9435   if (setup_value != NULL)
9436     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9437
9438   return hide_setup_token;
9439 }
9440
9441 void setHideSetupEntry(void *setup_value)
9442 {
9443   char *hide_setup_token = getHideSetupToken(setup_value);
9444
9445   if (setup_value != NULL)
9446     setHashEntry(hide_setup_hash, hide_setup_token, "");
9447 }
9448
9449 boolean hideSetupEntry(void *setup_value)
9450 {
9451   char *hide_setup_token = getHideSetupToken(setup_value);
9452
9453   return (setup_value != NULL &&
9454           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9455 }
9456
9457 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9458                                       struct TokenInfo *token_info,
9459                                       int token_nr, char *token_text)
9460 {
9461   char *token_hide_text = getStringCat2(token_text, ".hide");
9462   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9463
9464   // set the value of this setup option in the setup option structure
9465   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9466
9467   // check if this setup option should be hidden in the setup menu
9468   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9469     setHideSetupEntry(token_info[token_nr].value);
9470 }
9471
9472 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9473                                       struct TokenInfo *token_info,
9474                                       int token_nr)
9475 {
9476   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9477                             token_info[token_nr].text);
9478 }
9479
9480 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9481 {
9482   int i, pnr;
9483
9484   if (!setup_file_hash)
9485     return;
9486
9487   if (hide_setup_hash == NULL)
9488     hide_setup_hash = newSetupFileHash();
9489
9490   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9491     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9492
9493   setup.touch.grid_initialized = TRUE;
9494   for (i = 0; i < 2; i++)
9495   {
9496     int grid_xsize = setup.touch.grid_xsize[i];
9497     int grid_ysize = setup.touch.grid_ysize[i];
9498     int x, y;
9499
9500     // if virtual buttons are not loaded from setup file, repeat initializing
9501     // virtual buttons grid with default values later when video is initialized
9502     if (grid_xsize == -1 ||
9503         grid_ysize == -1)
9504     {
9505       setup.touch.grid_initialized = FALSE;
9506
9507       continue;
9508     }
9509
9510     for (y = 0; y < grid_ysize; y++)
9511     {
9512       char token_string[MAX_LINE_LEN];
9513
9514       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9515
9516       char *value_string = getHashEntry(setup_file_hash, token_string);
9517
9518       if (value_string == NULL)
9519         continue;
9520
9521       for (x = 0; x < grid_xsize; x++)
9522       {
9523         char c = value_string[x];
9524
9525         setup.touch.grid_button[i][x][y] =
9526           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9527       }
9528     }
9529   }
9530
9531   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9532     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9533
9534   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9535     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9536
9537   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9538   {
9539     char prefix[30];
9540
9541     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9542
9543     setup_input = setup.input[pnr];
9544     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9545     {
9546       char full_token[100];
9547
9548       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9549       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9550                                 full_token);
9551     }
9552     setup.input[pnr] = setup_input;
9553   }
9554
9555   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9556     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9557
9558   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9559     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9560
9561   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9562     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9563
9564   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9565     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9566
9567   setHideRelatedSetupEntries();
9568 }
9569
9570 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9571 {
9572   int i;
9573
9574   if (!setup_file_hash)
9575     return;
9576
9577   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9578     setSetupInfo(auto_setup_tokens, i,
9579                  getHashEntry(setup_file_hash,
9580                               auto_setup_tokens[i].text));
9581 }
9582
9583 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9584 {
9585   int i;
9586
9587   if (!setup_file_hash)
9588     return;
9589
9590   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9591     setSetupInfo(editor_cascade_setup_tokens, i,
9592                  getHashEntry(setup_file_hash,
9593                               editor_cascade_setup_tokens[i].text));
9594 }
9595
9596 void LoadSetupFromFilename(char *filename)
9597 {
9598   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9599
9600   if (setup_file_hash)
9601   {
9602     decodeSetupFileHash(setup_file_hash);
9603
9604     freeSetupFileHash(setup_file_hash);
9605   }
9606   else
9607   {
9608     Error(ERR_DEBUG, "using default setup values");
9609   }
9610 }
9611
9612 static void LoadSetup_SpecialPostProcessing(void)
9613 {
9614   char *player_name_new;
9615
9616   // needed to work around problems with fixed length strings
9617   player_name_new = get_corrected_login_name(setup.player_name);
9618   free(setup.player_name);
9619   setup.player_name = player_name_new;
9620
9621   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9622   if (setup.scroll_delay == FALSE)
9623   {
9624     setup.scroll_delay_value = MIN_SCROLL_DELAY;
9625     setup.scroll_delay = TRUE;                  // now always "on"
9626   }
9627
9628   // make sure that scroll delay value stays inside valid range
9629   setup.scroll_delay_value =
9630     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9631 }
9632
9633 void LoadSetup(void)
9634 {
9635   char *filename;
9636
9637   // always start with reliable default values
9638   setSetupInfoToDefaults(&setup);
9639
9640   // try to load setup values from default setup file
9641   filename = getDefaultSetupFilename();
9642
9643   if (fileExists(filename))
9644     LoadSetupFromFilename(filename);
9645
9646   // try to load setup values from user setup file
9647   filename = getSetupFilename();
9648
9649   LoadSetupFromFilename(filename);
9650
9651   LoadSetup_SpecialPostProcessing();
9652 }
9653
9654 void LoadSetup_AutoSetup(void)
9655 {
9656   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9657   SetupFileHash *setup_file_hash = NULL;
9658
9659   // always start with reliable default values
9660   setSetupInfoToDefaults_AutoSetup(&setup);
9661
9662   setup_file_hash = loadSetupFileHash(filename);
9663
9664   if (setup_file_hash)
9665   {
9666     decodeSetupFileHash_AutoSetup(setup_file_hash);
9667
9668     freeSetupFileHash(setup_file_hash);
9669   }
9670
9671   free(filename);
9672 }
9673
9674 void LoadSetup_EditorCascade(void)
9675 {
9676   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9677   SetupFileHash *setup_file_hash = NULL;
9678
9679   // always start with reliable default values
9680   setSetupInfoToDefaults_EditorCascade(&setup);
9681
9682   setup_file_hash = loadSetupFileHash(filename);
9683
9684   if (setup_file_hash)
9685   {
9686     decodeSetupFileHash_EditorCascade(setup_file_hash);
9687
9688     freeSetupFileHash(setup_file_hash);
9689   }
9690
9691   free(filename);
9692 }
9693
9694 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9695                                            char *mapping_line)
9696 {
9697   char mapping_guid[MAX_LINE_LEN];
9698   char *mapping_start, *mapping_end;
9699
9700   // get GUID from game controller mapping line: copy complete line
9701   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9702   mapping_guid[MAX_LINE_LEN - 1] = '\0';
9703
9704   // get GUID from game controller mapping line: cut after GUID part
9705   mapping_start = strchr(mapping_guid, ',');
9706   if (mapping_start != NULL)
9707     *mapping_start = '\0';
9708
9709   // cut newline from game controller mapping line
9710   mapping_end = strchr(mapping_line, '\n');
9711   if (mapping_end != NULL)
9712     *mapping_end = '\0';
9713
9714   // add mapping entry to game controller mappings hash
9715   setHashEntry(mappings_hash, mapping_guid, mapping_line);
9716 }
9717
9718 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9719                                                  char *filename)
9720 {
9721   FILE *file;
9722
9723   if (!(file = fopen(filename, MODE_READ)))
9724   {
9725     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9726
9727     return;
9728   }
9729
9730   while (!feof(file))
9731   {
9732     char line[MAX_LINE_LEN];
9733
9734     if (!fgets(line, MAX_LINE_LEN, file))
9735       break;
9736
9737     addGameControllerMappingToHash(mappings_hash, line);
9738   }
9739
9740   fclose(file);
9741 }
9742
9743 void SaveSetup(void)
9744 {
9745   char *filename = getSetupFilename();
9746   FILE *file;
9747   int i, pnr;
9748
9749   InitUserDataDirectory();
9750
9751   if (!(file = fopen(filename, MODE_WRITE)))
9752   {
9753     Error(ERR_WARN, "cannot write setup file '%s'", filename);
9754     return;
9755   }
9756
9757   fprintFileHeader(file, SETUP_FILENAME);
9758
9759   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9760   {
9761     // just to make things nicer :)
9762     if (global_setup_tokens[i].value == &setup.sound                    ||
9763         global_setup_tokens[i].value == &setup.graphics_set             ||
9764         global_setup_tokens[i].value == &setup.volume_simple            ||
9765         global_setup_tokens[i].value == &setup.network_mode             ||
9766         global_setup_tokens[i].value == &setup.touch.control_type       ||
9767         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
9768         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9769       fprintf(file, "\n");
9770
9771     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9772   }
9773
9774   for (i = 0; i < 2; i++)
9775   {
9776     int grid_xsize = setup.touch.grid_xsize[i];
9777     int grid_ysize = setup.touch.grid_ysize[i];
9778     int x, y;
9779
9780     fprintf(file, "\n");
9781
9782     for (y = 0; y < grid_ysize; y++)
9783     {
9784       char token_string[MAX_LINE_LEN];
9785       char value_string[MAX_LINE_LEN];
9786
9787       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9788
9789       for (x = 0; x < grid_xsize; x++)
9790       {
9791         char c = setup.touch.grid_button[i][x][y];
9792
9793         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9794       }
9795
9796       value_string[grid_xsize] = '\0';
9797
9798       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9799     }
9800   }
9801
9802   fprintf(file, "\n");
9803   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9804     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9805
9806   fprintf(file, "\n");
9807   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9808     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9809
9810   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9811   {
9812     char prefix[30];
9813
9814     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9815     fprintf(file, "\n");
9816
9817     setup_input = setup.input[pnr];
9818     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9819       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9820   }
9821
9822   fprintf(file, "\n");
9823   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9824     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9825
9826   // (internal setup values not saved to user setup file)
9827
9828   fprintf(file, "\n");
9829   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9830     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9831
9832   fprintf(file, "\n");
9833   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9834     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9835
9836   fclose(file);
9837
9838   SetFilePermissions(filename, PERMS_PRIVATE);
9839 }
9840
9841 void SaveSetup_AutoSetup(void)
9842 {
9843   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9844   FILE *file;
9845   int i;
9846
9847   InitUserDataDirectory();
9848
9849   if (!(file = fopen(filename, MODE_WRITE)))
9850   {
9851     Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9852     free(filename);
9853     return;
9854   }
9855
9856   fprintFileHeader(file, AUTOSETUP_FILENAME);
9857
9858   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9859     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9860
9861   fclose(file);
9862
9863   SetFilePermissions(filename, PERMS_PRIVATE);
9864
9865   free(filename);
9866 }
9867
9868 void SaveSetup_EditorCascade(void)
9869 {
9870   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9871   FILE *file;
9872   int i;
9873
9874   InitUserDataDirectory();
9875
9876   if (!(file = fopen(filename, MODE_WRITE)))
9877   {
9878     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9879     free(filename);
9880     return;
9881   }
9882
9883   fprintFileHeader(file, EDITORCASCADE_FILENAME);
9884
9885   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9886     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9887
9888   fclose(file);
9889
9890   SetFilePermissions(filename, PERMS_PRIVATE);
9891
9892   free(filename);
9893 }
9894
9895 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9896                                                   char *filename)
9897 {
9898   FILE *file;
9899
9900   if (!(file = fopen(filename, MODE_WRITE)))
9901   {
9902     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9903
9904     return;
9905   }
9906
9907   BEGIN_HASH_ITERATION(mappings_hash, itr)
9908   {
9909     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9910   }
9911   END_HASH_ITERATION(mappings_hash, itr)
9912
9913   fclose(file);
9914 }
9915
9916 void SaveSetup_AddGameControllerMapping(char *mapping)
9917 {
9918   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9919   SetupFileHash *mappings_hash = newSetupFileHash();
9920
9921   InitUserDataDirectory();
9922
9923   // load existing personal game controller mappings
9924   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9925
9926   // add new mapping to personal game controller mappings
9927   addGameControllerMappingToHash(mappings_hash, mapping);
9928
9929   // save updated personal game controller mappings
9930   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9931
9932   freeSetupFileHash(mappings_hash);
9933   free(filename);
9934 }
9935
9936 void LoadCustomElementDescriptions(void)
9937 {
9938   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9939   SetupFileHash *setup_file_hash;
9940   int i;
9941
9942   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9943   {
9944     if (element_info[i].custom_description != NULL)
9945     {
9946       free(element_info[i].custom_description);
9947       element_info[i].custom_description = NULL;
9948     }
9949   }
9950
9951   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9952     return;
9953
9954   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9955   {
9956     char *token = getStringCat2(element_info[i].token_name, ".name");
9957     char *value = getHashEntry(setup_file_hash, token);
9958
9959     if (value != NULL)
9960       element_info[i].custom_description = getStringCopy(value);
9961
9962     free(token);
9963   }
9964
9965   freeSetupFileHash(setup_file_hash);
9966 }
9967
9968 static int getElementFromToken(char *token)
9969 {
9970   char *value = getHashEntry(element_token_hash, token);
9971
9972   if (value != NULL)
9973     return atoi(value);
9974
9975   Error(ERR_WARN, "unknown element token '%s'", token);
9976
9977   return EL_UNDEFINED;
9978 }
9979
9980 void FreeGlobalAnimEventInfo(void)
9981 {
9982   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9983
9984   if (gaei->event_list == NULL)
9985     return;
9986
9987   int i;
9988
9989   for (i = 0; i < gaei->num_event_lists; i++)
9990   {
9991     checked_free(gaei->event_list[i]->event_value);
9992     checked_free(gaei->event_list[i]);
9993   }
9994
9995   checked_free(gaei->event_list);
9996
9997   gaei->event_list = NULL;
9998   gaei->num_event_lists = 0;
9999 }
10000
10001 static int AddGlobalAnimEventList(void)
10002 {
10003   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10004   int list_pos = gaei->num_event_lists++;
10005
10006   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10007                                      sizeof(struct GlobalAnimEventListInfo *));
10008
10009   gaei->event_list[list_pos] =
10010     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10011
10012   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10013
10014   gaeli->event_value = NULL;
10015   gaeli->num_event_values = 0;
10016
10017   return list_pos;
10018 }
10019
10020 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10021 {
10022   // do not add empty global animation events
10023   if (event_value == ANIM_EVENT_NONE)
10024     return list_pos;
10025
10026   // if list position is undefined, create new list
10027   if (list_pos == ANIM_EVENT_UNDEFINED)
10028     list_pos = AddGlobalAnimEventList();
10029
10030   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10031   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10032   int value_pos = gaeli->num_event_values++;
10033
10034   gaeli->event_value = checked_realloc(gaeli->event_value,
10035                                        gaeli->num_event_values * sizeof(int *));
10036
10037   gaeli->event_value[value_pos] = event_value;
10038
10039   return list_pos;
10040 }
10041
10042 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10043 {
10044   if (list_pos == ANIM_EVENT_UNDEFINED)
10045     return ANIM_EVENT_NONE;
10046
10047   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10048   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10049
10050   return gaeli->event_value[value_pos];
10051 }
10052
10053 int GetGlobalAnimEventValueCount(int list_pos)
10054 {
10055   if (list_pos == ANIM_EVENT_UNDEFINED)
10056     return 0;
10057
10058   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10059   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10060
10061   return gaeli->num_event_values;
10062 }
10063
10064 // This function checks if a string <s> of the format "string1, string2, ..."
10065 // exactly contains a string <s_contained>.
10066
10067 static boolean string_has_parameter(char *s, char *s_contained)
10068 {
10069   char *substring;
10070
10071   if (s == NULL || s_contained == NULL)
10072     return FALSE;
10073
10074   if (strlen(s_contained) > strlen(s))
10075     return FALSE;
10076
10077   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10078   {
10079     char next_char = s[strlen(s_contained)];
10080
10081     // check if next character is delimiter or whitespace
10082     return (next_char == ',' || next_char == '\0' ||
10083             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10084   }
10085
10086   // check if string contains another parameter string after a comma
10087   substring = strchr(s, ',');
10088   if (substring == NULL)        // string does not contain a comma
10089     return FALSE;
10090
10091   // advance string pointer to next character after the comma
10092   substring++;
10093
10094   // skip potential whitespaces after the comma
10095   while (*substring == ' ' || *substring == '\t')
10096     substring++;
10097
10098   return string_has_parameter(substring, s_contained);
10099 }
10100
10101 static int get_anim_parameter_value(char *s)
10102 {
10103   int event_value[] =
10104   {
10105     ANIM_EVENT_CLICK,
10106     ANIM_EVENT_INIT,
10107     ANIM_EVENT_START,
10108     ANIM_EVENT_END,
10109     ANIM_EVENT_POST
10110   };
10111   char *pattern_1[] =
10112   {
10113     "click:anim_",
10114     "init:anim_",
10115     "start:anim_",
10116     "end:anim_",
10117     "post:anim_"
10118   };
10119   char *pattern_2 = ".part_";
10120   char *matching_char = NULL;
10121   char *s_ptr = s;
10122   int pattern_1_len = 0;
10123   int result = ANIM_EVENT_NONE;
10124   int i;
10125
10126   for (i = 0; i < ARRAY_SIZE(event_value); i++)
10127   {
10128     matching_char = strstr(s_ptr, pattern_1[i]);
10129     pattern_1_len = strlen(pattern_1[i]);
10130     result = event_value[i];
10131
10132     if (matching_char != NULL)
10133       break;
10134   }
10135
10136   if (matching_char == NULL)
10137     return ANIM_EVENT_NONE;
10138
10139   s_ptr = matching_char + pattern_1_len;
10140
10141   // check for main animation number ("anim_X" or "anim_XX")
10142   if (*s_ptr >= '0' && *s_ptr <= '9')
10143   {
10144     int gic_anim_nr = (*s_ptr++ - '0');
10145
10146     if (*s_ptr >= '0' && *s_ptr <= '9')
10147       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10148
10149     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10150       return ANIM_EVENT_NONE;
10151
10152     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10153   }
10154   else
10155   {
10156     // invalid main animation number specified
10157
10158     return ANIM_EVENT_NONE;
10159   }
10160
10161   // check for animation part number ("part_X" or "part_XX") (optional)
10162   if (strPrefix(s_ptr, pattern_2))
10163   {
10164     s_ptr += strlen(pattern_2);
10165
10166     if (*s_ptr >= '0' && *s_ptr <= '9')
10167     {
10168       int gic_part_nr = (*s_ptr++ - '0');
10169
10170       if (*s_ptr >= '0' && *s_ptr <= '9')
10171         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10172
10173       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10174         return ANIM_EVENT_NONE;
10175
10176       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10177     }
10178     else
10179     {
10180       // invalid animation part number specified
10181
10182       return ANIM_EVENT_NONE;
10183     }
10184   }
10185
10186   // discard result if next character is neither delimiter nor whitespace
10187   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10188         *s_ptr == ' ' || *s_ptr == '\t'))
10189     return ANIM_EVENT_NONE;
10190
10191   return result;
10192 }
10193
10194 static int get_anim_parameter_values(char *s)
10195 {
10196   int list_pos = ANIM_EVENT_UNDEFINED;
10197   int event_value = ANIM_EVENT_DEFAULT;
10198
10199   if (string_has_parameter(s, "any"))
10200     event_value |= ANIM_EVENT_ANY;
10201
10202   if (string_has_parameter(s, "click:self") ||
10203       string_has_parameter(s, "click") ||
10204       string_has_parameter(s, "self"))
10205     event_value |= ANIM_EVENT_SELF;
10206
10207   if (string_has_parameter(s, "unclick:any"))
10208     event_value |= ANIM_EVENT_UNCLICK_ANY;
10209
10210   // if animation event found, add it to global animation event list
10211   if (event_value != ANIM_EVENT_NONE)
10212     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10213
10214   while (s != NULL)
10215   {
10216     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10217     event_value = get_anim_parameter_value(s);
10218
10219     // if animation event found, add it to global animation event list
10220     if (event_value != ANIM_EVENT_NONE)
10221       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10222
10223     // continue with next part of the string, starting with next comma
10224     s = strchr(s + 1, ',');
10225   }
10226
10227   return list_pos;
10228 }
10229
10230 static int get_anim_action_parameter_value(char *token)
10231 {
10232   int result = getImageIDFromToken(token);
10233
10234   if (result == -1)
10235   {
10236     char *gfx_token = getStringCat2("gfx.", token);
10237
10238     result = getImageIDFromToken(gfx_token);
10239
10240     checked_free(gfx_token);
10241   }
10242
10243   if (result == -1)
10244   {
10245     Key key = getKeyFromX11KeyName(token);
10246
10247     if (key != KSYM_UNDEFINED)
10248       result = -(int)key;
10249   }
10250
10251   if (result == -1)
10252     result = ANIM_EVENT_ACTION_NONE;
10253
10254   return result;
10255 }
10256
10257 int get_parameter_value(char *value_raw, char *suffix, int type)
10258 {
10259   char *value = getStringToLower(value_raw);
10260   int result = 0;       // probably a save default value
10261
10262   if (strEqual(suffix, ".direction"))
10263   {
10264     result = (strEqual(value, "left")  ? MV_LEFT :
10265               strEqual(value, "right") ? MV_RIGHT :
10266               strEqual(value, "up")    ? MV_UP :
10267               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
10268   }
10269   else if (strEqual(suffix, ".position"))
10270   {
10271     result = (strEqual(value, "left")   ? POS_LEFT :
10272               strEqual(value, "right")  ? POS_RIGHT :
10273               strEqual(value, "top")    ? POS_TOP :
10274               strEqual(value, "upper")  ? POS_UPPER :
10275               strEqual(value, "middle") ? POS_MIDDLE :
10276               strEqual(value, "lower")  ? POS_LOWER :
10277               strEqual(value, "bottom") ? POS_BOTTOM :
10278               strEqual(value, "any")    ? POS_ANY :
10279               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
10280   }
10281   else if (strEqual(suffix, ".align"))
10282   {
10283     result = (strEqual(value, "left")   ? ALIGN_LEFT :
10284               strEqual(value, "right")  ? ALIGN_RIGHT :
10285               strEqual(value, "center") ? ALIGN_CENTER :
10286               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10287   }
10288   else if (strEqual(suffix, ".valign"))
10289   {
10290     result = (strEqual(value, "top")    ? VALIGN_TOP :
10291               strEqual(value, "bottom") ? VALIGN_BOTTOM :
10292               strEqual(value, "middle") ? VALIGN_MIDDLE :
10293               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10294   }
10295   else if (strEqual(suffix, ".anim_mode"))
10296   {
10297     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
10298               string_has_parameter(value, "loop")       ? ANIM_LOOP :
10299               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
10300               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
10301               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
10302               string_has_parameter(value, "random")     ? ANIM_RANDOM :
10303               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
10304               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
10305               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
10306               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10307               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
10308               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
10309               string_has_parameter(value, "all")        ? ANIM_ALL :
10310               ANIM_DEFAULT);
10311
10312     if (string_has_parameter(value, "once"))
10313       result |= ANIM_ONCE;
10314
10315     if (string_has_parameter(value, "reverse"))
10316       result |= ANIM_REVERSE;
10317
10318     if (string_has_parameter(value, "opaque_player"))
10319       result |= ANIM_OPAQUE_PLAYER;
10320
10321     if (string_has_parameter(value, "static_panel"))
10322       result |= ANIM_STATIC_PANEL;
10323   }
10324   else if (strEqual(suffix, ".init_event") ||
10325            strEqual(suffix, ".anim_event"))
10326   {
10327     result = get_anim_parameter_values(value);
10328   }
10329   else if (strEqual(suffix, ".init_delay_action") ||
10330            strEqual(suffix, ".anim_delay_action") ||
10331            strEqual(suffix, ".post_delay_action") ||
10332            strEqual(suffix, ".init_event_action") ||
10333            strEqual(suffix, ".anim_event_action"))
10334   {
10335     result = get_anim_action_parameter_value(value_raw);
10336   }
10337   else if (strEqual(suffix, ".class"))
10338   {
10339     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10340               get_hash_from_key(value));
10341   }
10342   else if (strEqual(suffix, ".style"))
10343   {
10344     result = STYLE_DEFAULT;
10345
10346     if (string_has_parameter(value, "accurate_borders"))
10347       result |= STYLE_ACCURATE_BORDERS;
10348
10349     if (string_has_parameter(value, "inner_corners"))
10350       result |= STYLE_INNER_CORNERS;
10351
10352     if (string_has_parameter(value, "reverse"))
10353       result |= STYLE_REVERSE;
10354
10355     if (string_has_parameter(value, "block_clicks"))
10356       result |= STYLE_BLOCK;
10357
10358     if (string_has_parameter(value, "passthrough_clicks"))
10359       result |= STYLE_PASSTHROUGH;
10360
10361     if (string_has_parameter(value, "multiple_actions"))
10362       result |= STYLE_MULTIPLE_ACTIONS;
10363   }
10364   else if (strEqual(suffix, ".fade_mode"))
10365   {
10366     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
10367               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
10368               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
10369               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
10370               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
10371               FADE_MODE_DEFAULT);
10372   }
10373   else if (strEqual(suffix, ".auto_delay_unit"))
10374   {
10375     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
10376               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10377               AUTO_DELAY_UNIT_DEFAULT);
10378   }
10379   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
10380   {
10381     result = gfx.get_font_from_token_function(value);
10382   }
10383   else          // generic parameter of type integer or boolean
10384   {
10385     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10386               type == TYPE_INTEGER ? get_integer_from_string(value) :
10387               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10388               ARG_UNDEFINED_VALUE);
10389   }
10390
10391   free(value);
10392
10393   return result;
10394 }
10395
10396 static int get_token_parameter_value(char *token, char *value_raw)
10397 {
10398   char *suffix;
10399
10400   if (token == NULL || value_raw == NULL)
10401     return ARG_UNDEFINED_VALUE;
10402
10403   suffix = strrchr(token, '.');
10404   if (suffix == NULL)
10405     suffix = token;
10406
10407   if (strEqual(suffix, ".element"))
10408     return getElementFromToken(value_raw);
10409
10410   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10411   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10412 }
10413
10414 void InitMenuDesignSettings_Static(void)
10415 {
10416   int i;
10417
10418   // always start with reliable default values from static default config
10419   for (i = 0; image_config_vars[i].token != NULL; i++)
10420   {
10421     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10422
10423     if (value != NULL)
10424       *image_config_vars[i].value =
10425         get_token_parameter_value(image_config_vars[i].token, value);
10426   }
10427 }
10428
10429 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10430 {
10431   int i;
10432
10433   // the following initializes hierarchical values from static configuration
10434
10435   // special case: initialize "ARG_DEFAULT" values in static default config
10436   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10437   titlescreen_initial_first_default.fade_mode  =
10438     title_initial_first_default.fade_mode;
10439   titlescreen_initial_first_default.fade_delay =
10440     title_initial_first_default.fade_delay;
10441   titlescreen_initial_first_default.post_delay =
10442     title_initial_first_default.post_delay;
10443   titlescreen_initial_first_default.auto_delay =
10444     title_initial_first_default.auto_delay;
10445   titlescreen_initial_first_default.auto_delay_unit =
10446     title_initial_first_default.auto_delay_unit;
10447   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
10448   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10449   titlescreen_first_default.post_delay = title_first_default.post_delay;
10450   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10451   titlescreen_first_default.auto_delay_unit =
10452     title_first_default.auto_delay_unit;
10453   titlemessage_initial_first_default.fade_mode  =
10454     title_initial_first_default.fade_mode;
10455   titlemessage_initial_first_default.fade_delay =
10456     title_initial_first_default.fade_delay;
10457   titlemessage_initial_first_default.post_delay =
10458     title_initial_first_default.post_delay;
10459   titlemessage_initial_first_default.auto_delay =
10460     title_initial_first_default.auto_delay;
10461   titlemessage_initial_first_default.auto_delay_unit =
10462     title_initial_first_default.auto_delay_unit;
10463   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
10464   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10465   titlemessage_first_default.post_delay = title_first_default.post_delay;
10466   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10467   titlemessage_first_default.auto_delay_unit =
10468     title_first_default.auto_delay_unit;
10469
10470   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
10471   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10472   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10473   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10474   titlescreen_initial_default.auto_delay_unit =
10475     title_initial_default.auto_delay_unit;
10476   titlescreen_default.fade_mode  = title_default.fade_mode;
10477   titlescreen_default.fade_delay = title_default.fade_delay;
10478   titlescreen_default.post_delay = title_default.post_delay;
10479   titlescreen_default.auto_delay = title_default.auto_delay;
10480   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10481   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
10482   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10483   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10484   titlemessage_initial_default.auto_delay_unit =
10485     title_initial_default.auto_delay_unit;
10486   titlemessage_default.fade_mode  = title_default.fade_mode;
10487   titlemessage_default.fade_delay = title_default.fade_delay;
10488   titlemessage_default.post_delay = title_default.post_delay;
10489   titlemessage_default.auto_delay = title_default.auto_delay;
10490   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10491
10492   // special case: initialize "ARG_DEFAULT" values in static default config
10493   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10494   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10495   {
10496     titlescreen_initial_first[i] = titlescreen_initial_first_default;
10497     titlescreen_first[i] = titlescreen_first_default;
10498     titlemessage_initial_first[i] = titlemessage_initial_first_default;
10499     titlemessage_first[i] = titlemessage_first_default;
10500
10501     titlescreen_initial[i] = titlescreen_initial_default;
10502     titlescreen[i] = titlescreen_default;
10503     titlemessage_initial[i] = titlemessage_initial_default;
10504     titlemessage[i] = titlemessage_default;
10505   }
10506
10507   // special case: initialize "ARG_DEFAULT" values in static default config
10508   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10509   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10510   {
10511     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
10512       continue;
10513
10514     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10515     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10516     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10517   }
10518
10519   // special case: initialize "ARG_DEFAULT" values in static default config
10520   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10521   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10522   {
10523     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10524     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10525     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10526
10527     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
10528       continue;
10529
10530     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10531   }
10532 }
10533
10534 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10535 {
10536   static struct
10537   {
10538     struct XY *dst, *src;
10539   }
10540   game_buttons_xy[] =
10541   {
10542     { &game.button.save,        &game.button.stop       },
10543     { &game.button.pause2,      &game.button.pause      },
10544     { &game.button.load,        &game.button.play       },
10545     { &game.button.undo,        &game.button.stop       },
10546     { &game.button.redo,        &game.button.play       },
10547
10548     { NULL,                     NULL                    }
10549   };
10550   int i, j;
10551
10552   // special case: initialize later added SETUP list size from LEVELS value
10553   if (menu.list_size[GAME_MODE_SETUP] == -1)
10554     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10555
10556   // set default position for snapshot buttons to stop/pause/play buttons
10557   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10558     if ((*game_buttons_xy[i].dst).x == -1 &&
10559         (*game_buttons_xy[i].dst).y == -1)
10560       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10561
10562   // --------------------------------------------------------------------------
10563   // dynamic viewports (including playfield margins, borders and alignments)
10564   // --------------------------------------------------------------------------
10565
10566   // dynamic viewports currently only supported for landscape mode
10567   int display_width  = MAX(video.display_width, video.display_height);
10568   int display_height = MIN(video.display_width, video.display_height);
10569
10570   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10571   {
10572     struct RectWithBorder *vp_window    = &viewport.window[i];
10573     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10574     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
10575     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
10576     boolean dynamic_window_width     = (vp_window->min_width     != -1);
10577     boolean dynamic_window_height    = (vp_window->min_height    != -1);
10578     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
10579     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10580
10581     // adjust window size if min/max width/height is specified
10582
10583     if (vp_window->min_width != -1)
10584     {
10585       int window_width = display_width;
10586
10587       // when using static window height, use aspect ratio of display
10588       if (vp_window->min_height == -1)
10589         window_width = vp_window->height * display_width / display_height;
10590
10591       vp_window->width = MAX(vp_window->min_width, window_width);
10592     }
10593
10594     if (vp_window->min_height != -1)
10595     {
10596       int window_height = display_height;
10597
10598       // when using static window width, use aspect ratio of display
10599       if (vp_window->min_width == -1)
10600         window_height = vp_window->width * display_height / display_width;
10601
10602       vp_window->height = MAX(vp_window->min_height, window_height);
10603     }
10604
10605     if (vp_window->max_width != -1)
10606       vp_window->width = MIN(vp_window->width, vp_window->max_width);
10607
10608     if (vp_window->max_height != -1)
10609       vp_window->height = MIN(vp_window->height, vp_window->max_height);
10610
10611     int playfield_width  = vp_window->width;
10612     int playfield_height = vp_window->height;
10613
10614     // adjust playfield size and position according to specified margins
10615
10616     playfield_width  -= vp_playfield->margin_left;
10617     playfield_width  -= vp_playfield->margin_right;
10618
10619     playfield_height -= vp_playfield->margin_top;
10620     playfield_height -= vp_playfield->margin_bottom;
10621
10622     // adjust playfield size if min/max width/height is specified
10623
10624     if (vp_playfield->min_width != -1)
10625       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10626
10627     if (vp_playfield->min_height != -1)
10628       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10629
10630     if (vp_playfield->max_width != -1)
10631       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10632
10633     if (vp_playfield->max_height != -1)
10634       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10635
10636     // adjust playfield position according to specified alignment
10637
10638     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10639       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10640     else if (vp_playfield->align == ALIGN_CENTER)
10641       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10642     else if (vp_playfield->align == ALIGN_RIGHT)
10643       vp_playfield->x += playfield_width - vp_playfield->width;
10644
10645     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10646       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10647     else if (vp_playfield->valign == VALIGN_MIDDLE)
10648       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10649     else if (vp_playfield->valign == VALIGN_BOTTOM)
10650       vp_playfield->y += playfield_height - vp_playfield->height;
10651
10652     vp_playfield->x += vp_playfield->margin_left;
10653     vp_playfield->y += vp_playfield->margin_top;
10654
10655     // adjust individual playfield borders if only default border is specified
10656
10657     if (vp_playfield->border_left == -1)
10658       vp_playfield->border_left = vp_playfield->border_size;
10659     if (vp_playfield->border_right == -1)
10660       vp_playfield->border_right = vp_playfield->border_size;
10661     if (vp_playfield->border_top == -1)
10662       vp_playfield->border_top = vp_playfield->border_size;
10663     if (vp_playfield->border_bottom == -1)
10664       vp_playfield->border_bottom = vp_playfield->border_size;
10665
10666     // set dynamic playfield borders if borders are specified as undefined
10667     // (but only if window size was dynamic and playfield size was static)
10668
10669     if (dynamic_window_width && !dynamic_playfield_width)
10670     {
10671       if (vp_playfield->border_left == -1)
10672       {
10673         vp_playfield->border_left = (vp_playfield->x -
10674                                      vp_playfield->margin_left);
10675         vp_playfield->x     -= vp_playfield->border_left;
10676         vp_playfield->width += vp_playfield->border_left;
10677       }
10678
10679       if (vp_playfield->border_right == -1)
10680       {
10681         vp_playfield->border_right = (vp_window->width -
10682                                       vp_playfield->x -
10683                                       vp_playfield->width -
10684                                       vp_playfield->margin_right);
10685         vp_playfield->width += vp_playfield->border_right;
10686       }
10687     }
10688
10689     if (dynamic_window_height && !dynamic_playfield_height)
10690     {
10691       if (vp_playfield->border_top == -1)
10692       {
10693         vp_playfield->border_top = (vp_playfield->y -
10694                                     vp_playfield->margin_top);
10695         vp_playfield->y      -= vp_playfield->border_top;
10696         vp_playfield->height += vp_playfield->border_top;
10697       }
10698
10699       if (vp_playfield->border_bottom == -1)
10700       {
10701         vp_playfield->border_bottom = (vp_window->height -
10702                                        vp_playfield->y -
10703                                        vp_playfield->height -
10704                                        vp_playfield->margin_bottom);
10705         vp_playfield->height += vp_playfield->border_bottom;
10706       }
10707     }
10708
10709     // adjust playfield size to be a multiple of a defined alignment tile size
10710
10711     int align_size = vp_playfield->align_size;
10712     int playfield_xtiles = vp_playfield->width  / align_size;
10713     int playfield_ytiles = vp_playfield->height / align_size;
10714     int playfield_width_corrected  = playfield_xtiles * align_size;
10715     int playfield_height_corrected = playfield_ytiles * align_size;
10716     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10717                                  i == GFX_SPECIAL_ARG_EDITOR);
10718
10719     if (is_playfield_mode &&
10720         dynamic_playfield_width &&
10721         vp_playfield->width != playfield_width_corrected)
10722     {
10723       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10724
10725       vp_playfield->width = playfield_width_corrected;
10726
10727       if (vp_playfield->align == ALIGN_LEFT)
10728       {
10729         vp_playfield->border_left += playfield_xdiff;
10730       }
10731       else if (vp_playfield->align == ALIGN_RIGHT)
10732       {
10733         vp_playfield->border_right += playfield_xdiff;
10734       }
10735       else if (vp_playfield->align == ALIGN_CENTER)
10736       {
10737         int border_left_diff  = playfield_xdiff / 2;
10738         int border_right_diff = playfield_xdiff - border_left_diff;
10739
10740         vp_playfield->border_left  += border_left_diff;
10741         vp_playfield->border_right += border_right_diff;
10742       }
10743     }
10744
10745     if (is_playfield_mode &&
10746         dynamic_playfield_height &&
10747         vp_playfield->height != playfield_height_corrected)
10748     {
10749       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10750
10751       vp_playfield->height = playfield_height_corrected;
10752
10753       if (vp_playfield->valign == VALIGN_TOP)
10754       {
10755         vp_playfield->border_top += playfield_ydiff;
10756       }
10757       else if (vp_playfield->align == VALIGN_BOTTOM)
10758       {
10759         vp_playfield->border_right += playfield_ydiff;
10760       }
10761       else if (vp_playfield->align == VALIGN_MIDDLE)
10762       {
10763         int border_top_diff    = playfield_ydiff / 2;
10764         int border_bottom_diff = playfield_ydiff - border_top_diff;
10765
10766         vp_playfield->border_top    += border_top_diff;
10767         vp_playfield->border_bottom += border_bottom_diff;
10768       }
10769     }
10770
10771     // adjust door positions according to specified alignment
10772
10773     for (j = 0; j < 2; j++)
10774     {
10775       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10776
10777       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10778         vp_door->x = ALIGNED_VP_XPOS(vp_door);
10779       else if (vp_door->align == ALIGN_CENTER)
10780         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10781       else if (vp_door->align == ALIGN_RIGHT)
10782         vp_door->x += vp_window->width - vp_door->width;
10783
10784       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10785         vp_door->y = ALIGNED_VP_YPOS(vp_door);
10786       else if (vp_door->valign == VALIGN_MIDDLE)
10787         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10788       else if (vp_door->valign == VALIGN_BOTTOM)
10789         vp_door->y += vp_window->height - vp_door->height;
10790     }
10791   }
10792 }
10793
10794 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10795 {
10796   static struct
10797   {
10798     struct XYTileSize *dst, *src;
10799     int graphic;
10800   }
10801   editor_buttons_xy[] =
10802   {
10803     {
10804       &editor.button.element_left,      &editor.palette.element_left,
10805       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10806     },
10807     {
10808       &editor.button.element_middle,    &editor.palette.element_middle,
10809       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10810     },
10811     {
10812       &editor.button.element_right,     &editor.palette.element_right,
10813       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10814     },
10815
10816     { NULL,                     NULL                    }
10817   };
10818   int i;
10819
10820   // set default position for element buttons to element graphics
10821   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10822   {
10823     if ((*editor_buttons_xy[i].dst).x == -1 &&
10824         (*editor_buttons_xy[i].dst).y == -1)
10825     {
10826       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10827
10828       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10829
10830       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10831     }
10832   }
10833
10834   // adjust editor palette rows and columns if specified to be dynamic
10835
10836   if (editor.palette.cols == -1)
10837   {
10838     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10839     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10840     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10841
10842     editor.palette.cols = (vp_width - sc_width) / bt_width;
10843
10844     if (editor.palette.x == -1)
10845     {
10846       int palette_width = editor.palette.cols * bt_width + sc_width;
10847
10848       editor.palette.x = (vp_width - palette_width) / 2;
10849     }
10850   }
10851
10852   if (editor.palette.rows == -1)
10853   {
10854     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
10855     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
10856     int tx_height = getFontHeight(FONT_TEXT_2);
10857
10858     editor.palette.rows = (vp_height - tx_height) / bt_height;
10859
10860     if (editor.palette.y == -1)
10861     {
10862       int palette_height = editor.palette.rows * bt_height + tx_height;
10863
10864       editor.palette.y = (vp_height - palette_height) / 2;
10865     }
10866   }
10867 }
10868
10869 static void LoadMenuDesignSettingsFromFilename(char *filename)
10870 {
10871   static struct TitleFadingInfo tfi;
10872   static struct TitleMessageInfo tmi;
10873   static struct TokenInfo title_tokens[] =
10874   {
10875     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
10876     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
10877     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
10878     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
10879     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
10880
10881     { -1,               NULL,                   NULL                    }
10882   };
10883   static struct TokenInfo titlemessage_tokens[] =
10884   {
10885     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
10886     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
10887     { TYPE_INTEGER,     &tmi.width,             ".width"                },
10888     { TYPE_INTEGER,     &tmi.height,            ".height"               },
10889     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
10890     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
10891     { TYPE_INTEGER,     &tmi.align,             ".align"                },
10892     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
10893     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
10894     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
10895     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
10896     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
10897     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
10898     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
10899     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
10900     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
10901     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
10902     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
10903
10904     { -1,               NULL,                   NULL                    }
10905   };
10906   static struct
10907   {
10908     struct TitleFadingInfo *info;
10909     char *text;
10910   }
10911   title_info[] =
10912   {
10913     // initialize first titles from "enter screen" definitions, if defined
10914     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
10915     { &title_first_default,             "menu.enter_screen.TITLE"       },
10916
10917     // initialize title screens from "next screen" definitions, if defined
10918     { &title_initial_default,           "menu.next_screen.TITLE"        },
10919     { &title_default,                   "menu.next_screen.TITLE"        },
10920
10921     { NULL,                             NULL                            }
10922   };
10923   static struct
10924   {
10925     struct TitleMessageInfo *array;
10926     char *text;
10927   }
10928   titlemessage_arrays[] =
10929   {
10930     // initialize first titles from "enter screen" definitions, if defined
10931     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
10932     { titlescreen_first,                "menu.enter_screen.TITLE"       },
10933     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
10934     { titlemessage_first,               "menu.enter_screen.TITLE"       },
10935
10936     // initialize titles from "next screen" definitions, if defined
10937     { titlescreen_initial,              "menu.next_screen.TITLE"        },
10938     { titlescreen,                      "menu.next_screen.TITLE"        },
10939     { titlemessage_initial,             "menu.next_screen.TITLE"        },
10940     { titlemessage,                     "menu.next_screen.TITLE"        },
10941
10942     // overwrite titles with title definitions, if defined
10943     { titlescreen_initial_first,        "[title_initial]"               },
10944     { titlescreen_first,                "[title]"                       },
10945     { titlemessage_initial_first,       "[title_initial]"               },
10946     { titlemessage_first,               "[title]"                       },
10947
10948     { titlescreen_initial,              "[title_initial]"               },
10949     { titlescreen,                      "[title]"                       },
10950     { titlemessage_initial,             "[title_initial]"               },
10951     { titlemessage,                     "[title]"                       },
10952
10953     // overwrite titles with title screen/message definitions, if defined
10954     { titlescreen_initial_first,        "[titlescreen_initial]"         },
10955     { titlescreen_first,                "[titlescreen]"                 },
10956     { titlemessage_initial_first,       "[titlemessage_initial]"        },
10957     { titlemessage_first,               "[titlemessage]"                },
10958
10959     { titlescreen_initial,              "[titlescreen_initial]"         },
10960     { titlescreen,                      "[titlescreen]"                 },
10961     { titlemessage_initial,             "[titlemessage_initial]"        },
10962     { titlemessage,                     "[titlemessage]"                },
10963
10964     { NULL,                             NULL                            }
10965   };
10966   SetupFileHash *setup_file_hash;
10967   int i, j, k;
10968
10969   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10970     return;
10971
10972   // the following initializes hierarchical values from dynamic configuration
10973
10974   // special case: initialize with default values that may be overwritten
10975   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
10976   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10977   {
10978     struct TokenIntPtrInfo menu_config[] =
10979     {
10980       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
10981       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
10982       { "menu.list_size",       &menu.list_size[i]      }
10983     };
10984
10985     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10986     {
10987       char *token = menu_config[j].token;
10988       char *value = getHashEntry(setup_file_hash, token);
10989
10990       if (value != NULL)
10991         *menu_config[j].value = get_integer_from_string(value);
10992     }
10993   }
10994
10995   // special case: initialize with default values that may be overwritten
10996   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
10997   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10998   {
10999     struct TokenIntPtrInfo menu_config[] =
11000     {
11001       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
11002       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
11003       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
11004     };
11005
11006     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11007     {
11008       char *token = menu_config[j].token;
11009       char *value = getHashEntry(setup_file_hash, token);
11010
11011       if (value != NULL)
11012         *menu_config[j].value = get_integer_from_string(value);
11013     }
11014   }
11015
11016   // special case: initialize with default values that may be overwritten
11017   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11018   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11019   {
11020     struct TokenIntPtrInfo menu_config[] =
11021     {
11022       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
11023       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
11024     };
11025
11026     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11027     {
11028       char *token = menu_config[j].token;
11029       char *value = getHashEntry(setup_file_hash, token);
11030
11031       if (value != NULL)
11032         *menu_config[j].value = get_integer_from_string(value);
11033     }
11034   }
11035
11036   // special case: initialize with default values that may be overwritten
11037   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11038   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11039   {
11040     struct TokenIntPtrInfo menu_config[] =
11041     {
11042       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
11043       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
11044       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
11045       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
11046       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
11047       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
11048       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
11049       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
11050       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
11051     };
11052
11053     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11054     {
11055       char *token = menu_config[j].token;
11056       char *value = getHashEntry(setup_file_hash, token);
11057
11058       if (value != NULL)
11059         *menu_config[j].value = get_integer_from_string(value);
11060     }
11061   }
11062
11063   // special case: initialize with default values that may be overwritten
11064   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11065   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11066   {
11067     struct TokenIntPtrInfo menu_config[] =
11068     {
11069       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
11070       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11071       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11072       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
11073       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11074       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11075       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
11076       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
11077       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
11078     };
11079
11080     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11081     {
11082       char *token = menu_config[j].token;
11083       char *value = getHashEntry(setup_file_hash, token);
11084
11085       if (value != NULL)
11086         *menu_config[j].value = get_token_parameter_value(token, value);
11087     }
11088   }
11089
11090   // special case: initialize with default values that may be overwritten
11091   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11092   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11093   {
11094     struct
11095     {
11096       char *token_prefix;
11097       struct RectWithBorder *struct_ptr;
11098     }
11099     vp_struct[] =
11100     {
11101       { "viewport.window",      &viewport.window[i]     },
11102       { "viewport.playfield",   &viewport.playfield[i]  },
11103       { "viewport.door_1",      &viewport.door_1[i]     },
11104       { "viewport.door_2",      &viewport.door_2[i]     }
11105     };
11106
11107     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11108     {
11109       struct TokenIntPtrInfo vp_config[] =
11110       {
11111         { ".x",                 &vp_struct[j].struct_ptr->x             },
11112         { ".y",                 &vp_struct[j].struct_ptr->y             },
11113         { ".width",             &vp_struct[j].struct_ptr->width         },
11114         { ".height",            &vp_struct[j].struct_ptr->height        },
11115         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
11116         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
11117         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
11118         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
11119         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
11120         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
11121         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
11122         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
11123         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
11124         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
11125         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
11126         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
11127         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
11128         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
11129         { ".align",             &vp_struct[j].struct_ptr->align         },
11130         { ".valign",            &vp_struct[j].struct_ptr->valign        }
11131       };
11132
11133       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11134       {
11135         char *token = getStringCat2(vp_struct[j].token_prefix,
11136                                     vp_config[k].token);
11137         char *value = getHashEntry(setup_file_hash, token);
11138
11139         if (value != NULL)
11140           *vp_config[k].value = get_token_parameter_value(token, value);
11141
11142         free(token);
11143       }
11144     }
11145   }
11146
11147   // special case: initialize with default values that may be overwritten
11148   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11149   for (i = 0; title_info[i].info != NULL; i++)
11150   {
11151     struct TitleFadingInfo *info = title_info[i].info;
11152     char *base_token = title_info[i].text;
11153
11154     for (j = 0; title_tokens[j].type != -1; j++)
11155     {
11156       char *token = getStringCat2(base_token, title_tokens[j].text);
11157       char *value = getHashEntry(setup_file_hash, token);
11158
11159       if (value != NULL)
11160       {
11161         int parameter_value = get_token_parameter_value(token, value);
11162
11163         tfi = *info;
11164
11165         *(int *)title_tokens[j].value = (int)parameter_value;
11166
11167         *info = tfi;
11168       }
11169
11170       free(token);
11171     }
11172   }
11173
11174   // special case: initialize with default values that may be overwritten
11175   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11176   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11177   {
11178     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11179     char *base_token = titlemessage_arrays[i].text;
11180
11181     for (j = 0; titlemessage_tokens[j].type != -1; j++)
11182     {
11183       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11184       char *value = getHashEntry(setup_file_hash, token);
11185
11186       if (value != NULL)
11187       {
11188         int parameter_value = get_token_parameter_value(token, value);
11189
11190         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11191         {
11192           tmi = array[k];
11193
11194           if (titlemessage_tokens[j].type == TYPE_INTEGER)
11195             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
11196           else
11197             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11198
11199           array[k] = tmi;
11200         }
11201       }
11202
11203       free(token);
11204     }
11205   }
11206
11207   // special case: check if network and preview player positions are redefined,
11208   // to compare this later against the main menu level preview being redefined
11209   struct TokenIntPtrInfo menu_config_players[] =
11210   {
11211     { "main.network_players.x", &menu.main.network_players.redefined    },
11212     { "main.network_players.y", &menu.main.network_players.redefined    },
11213     { "main.preview_players.x", &menu.main.preview_players.redefined    },
11214     { "main.preview_players.y", &menu.main.preview_players.redefined    },
11215     { "preview.x",              &preview.redefined                      },
11216     { "preview.y",              &preview.redefined                      }
11217   };
11218
11219   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11220     *menu_config_players[i].value = FALSE;
11221
11222   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11223     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11224       *menu_config_players[i].value = TRUE;
11225
11226   // read (and overwrite with) values that may be specified in config file
11227   for (i = 0; image_config_vars[i].token != NULL; i++)
11228   {
11229     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11230
11231     // (ignore definitions set to "[DEFAULT]" which are already initialized)
11232     if (value != NULL && !strEqual(value, ARG_DEFAULT))
11233       *image_config_vars[i].value =
11234         get_token_parameter_value(image_config_vars[i].token, value);
11235   }
11236
11237   freeSetupFileHash(setup_file_hash);
11238 }
11239
11240 void LoadMenuDesignSettings(void)
11241 {
11242   char *filename_base = UNDEFINED_FILENAME, *filename_local;
11243
11244   InitMenuDesignSettings_Static();
11245   InitMenuDesignSettings_SpecialPreProcessing();
11246
11247   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11248   {
11249     // first look for special settings configured in level series config
11250     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11251
11252     if (fileExists(filename_base))
11253       LoadMenuDesignSettingsFromFilename(filename_base);
11254   }
11255
11256   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11257
11258   if (filename_local != NULL && !strEqual(filename_base, filename_local))
11259     LoadMenuDesignSettingsFromFilename(filename_local);
11260
11261   InitMenuDesignSettings_SpecialPostProcessing();
11262 }
11263
11264 void LoadMenuDesignSettings_AfterGraphics(void)
11265 {
11266   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11267 }
11268
11269 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11270 {
11271   char *filename = getEditorSetupFilename();
11272   SetupFileList *setup_file_list, *list;
11273   SetupFileHash *element_hash;
11274   int num_unknown_tokens = 0;
11275   int i;
11276
11277   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11278     return;
11279
11280   element_hash = newSetupFileHash();
11281
11282   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11283     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11284
11285   // determined size may be larger than needed (due to unknown elements)
11286   *num_elements = 0;
11287   for (list = setup_file_list; list != NULL; list = list->next)
11288     (*num_elements)++;
11289
11290   // add space for up to 3 more elements for padding that may be needed
11291   *num_elements += 3;
11292
11293   // free memory for old list of elements, if needed
11294   checked_free(*elements);
11295
11296   // allocate memory for new list of elements
11297   *elements = checked_malloc(*num_elements * sizeof(int));
11298
11299   *num_elements = 0;
11300   for (list = setup_file_list; list != NULL; list = list->next)
11301   {
11302     char *value = getHashEntry(element_hash, list->token);
11303
11304     if (value == NULL)          // try to find obsolete token mapping
11305     {
11306       char *mapped_token = get_mapped_token(list->token);
11307
11308       if (mapped_token != NULL)
11309       {
11310         value = getHashEntry(element_hash, mapped_token);
11311
11312         free(mapped_token);
11313       }
11314     }
11315
11316     if (value != NULL)
11317     {
11318       (*elements)[(*num_elements)++] = atoi(value);
11319     }
11320     else
11321     {
11322       if (num_unknown_tokens == 0)
11323       {
11324         Error(ERR_INFO_LINE, "-");
11325         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11326         Error(ERR_INFO, "- config file: '%s'", filename);
11327
11328         num_unknown_tokens++;
11329       }
11330
11331       Error(ERR_INFO, "- token: '%s'", list->token);
11332     }
11333   }
11334
11335   if (num_unknown_tokens > 0)
11336     Error(ERR_INFO_LINE, "-");
11337
11338   while (*num_elements % 4)     // pad with empty elements, if needed
11339     (*elements)[(*num_elements)++] = EL_EMPTY;
11340
11341   freeSetupFileList(setup_file_list);
11342   freeSetupFileHash(element_hash);
11343
11344 #if 0
11345   for (i = 0; i < *num_elements; i++)
11346     printf("editor: element '%s' [%d]\n",
11347            element_info[(*elements)[i]].token_name, (*elements)[i]);
11348 #endif
11349 }
11350
11351 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11352                                                      boolean is_sound)
11353 {
11354   SetupFileHash *setup_file_hash = NULL;
11355   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11356   char *filename_music, *filename_prefix, *filename_info;
11357   struct
11358   {
11359     char *token;
11360     char **value_ptr;
11361   }
11362   token_to_value_ptr[] =
11363   {
11364     { "title_header",   &tmp_music_file_info.title_header       },
11365     { "artist_header",  &tmp_music_file_info.artist_header      },
11366     { "album_header",   &tmp_music_file_info.album_header       },
11367     { "year_header",    &tmp_music_file_info.year_header        },
11368
11369     { "title",          &tmp_music_file_info.title              },
11370     { "artist",         &tmp_music_file_info.artist             },
11371     { "album",          &tmp_music_file_info.album              },
11372     { "year",           &tmp_music_file_info.year               },
11373
11374     { NULL,             NULL                                    },
11375   };
11376   int i;
11377
11378   filename_music = (is_sound ? getCustomSoundFilename(basename) :
11379                     getCustomMusicFilename(basename));
11380
11381   if (filename_music == NULL)
11382     return NULL;
11383
11384   // ---------- try to replace file extension ----------
11385
11386   filename_prefix = getStringCopy(filename_music);
11387   if (strrchr(filename_prefix, '.') != NULL)
11388     *strrchr(filename_prefix, '.') = '\0';
11389   filename_info = getStringCat2(filename_prefix, ".txt");
11390
11391   if (fileExists(filename_info))
11392     setup_file_hash = loadSetupFileHash(filename_info);
11393
11394   free(filename_prefix);
11395   free(filename_info);
11396
11397   if (setup_file_hash == NULL)
11398   {
11399     // ---------- try to add file extension ----------
11400
11401     filename_prefix = getStringCopy(filename_music);
11402     filename_info = getStringCat2(filename_prefix, ".txt");
11403
11404     if (fileExists(filename_info))
11405       setup_file_hash = loadSetupFileHash(filename_info);
11406
11407     free(filename_prefix);
11408     free(filename_info);
11409   }
11410
11411   if (setup_file_hash == NULL)
11412     return NULL;
11413
11414   // ---------- music file info found ----------
11415
11416   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11417
11418   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11419   {
11420     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11421
11422     *token_to_value_ptr[i].value_ptr =
11423       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11424   }
11425
11426   tmp_music_file_info.basename = getStringCopy(basename);
11427   tmp_music_file_info.music = music;
11428   tmp_music_file_info.is_sound = is_sound;
11429
11430   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11431   *new_music_file_info = tmp_music_file_info;
11432
11433   return new_music_file_info;
11434 }
11435
11436 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11437 {
11438   return get_music_file_info_ext(basename, music, FALSE);
11439 }
11440
11441 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11442 {
11443   return get_music_file_info_ext(basename, sound, TRUE);
11444 }
11445
11446 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11447                                      char *basename, boolean is_sound)
11448 {
11449   for (; list != NULL; list = list->next)
11450     if (list->is_sound == is_sound && strEqual(list->basename, basename))
11451       return TRUE;
11452
11453   return FALSE;
11454 }
11455
11456 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11457 {
11458   return music_info_listed_ext(list, basename, FALSE);
11459 }
11460
11461 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11462 {
11463   return music_info_listed_ext(list, basename, TRUE);
11464 }
11465
11466 void LoadMusicInfo(void)
11467 {
11468   char *music_directory = getCustomMusicDirectory();
11469   int num_music = getMusicListSize();
11470   int num_music_noconf = 0;
11471   int num_sounds = getSoundListSize();
11472   Directory *dir;
11473   DirectoryEntry *dir_entry;
11474   struct FileInfo *music, *sound;
11475   struct MusicFileInfo *next, **new;
11476   int i;
11477
11478   while (music_file_info != NULL)
11479   {
11480     next = music_file_info->next;
11481
11482     checked_free(music_file_info->basename);
11483
11484     checked_free(music_file_info->title_header);
11485     checked_free(music_file_info->artist_header);
11486     checked_free(music_file_info->album_header);
11487     checked_free(music_file_info->year_header);
11488
11489     checked_free(music_file_info->title);
11490     checked_free(music_file_info->artist);
11491     checked_free(music_file_info->album);
11492     checked_free(music_file_info->year);
11493
11494     free(music_file_info);
11495
11496     music_file_info = next;
11497   }
11498
11499   new = &music_file_info;
11500
11501   for (i = 0; i < num_music; i++)
11502   {
11503     music = getMusicListEntry(i);
11504
11505     if (music->filename == NULL)
11506       continue;
11507
11508     if (strEqual(music->filename, UNDEFINED_FILENAME))
11509       continue;
11510
11511     // a configured file may be not recognized as music
11512     if (!FileIsMusic(music->filename))
11513       continue;
11514
11515     if (!music_info_listed(music_file_info, music->filename))
11516     {
11517       *new = get_music_file_info(music->filename, i);
11518
11519       if (*new != NULL)
11520         new = &(*new)->next;
11521     }
11522   }
11523
11524   if ((dir = openDirectory(music_directory)) == NULL)
11525   {
11526     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
11527     return;
11528   }
11529
11530   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
11531   {
11532     char *basename = dir_entry->basename;
11533     boolean music_already_used = FALSE;
11534     int i;
11535
11536     // skip all music files that are configured in music config file
11537     for (i = 0; i < num_music; i++)
11538     {
11539       music = getMusicListEntry(i);
11540
11541       if (music->filename == NULL)
11542         continue;
11543
11544       if (strEqual(basename, music->filename))
11545       {
11546         music_already_used = TRUE;
11547         break;
11548       }
11549     }
11550
11551     if (music_already_used)
11552       continue;
11553
11554     if (!FileIsMusic(dir_entry->filename))
11555       continue;
11556
11557     if (!music_info_listed(music_file_info, basename))
11558     {
11559       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11560
11561       if (*new != NULL)
11562         new = &(*new)->next;
11563     }
11564
11565     num_music_noconf++;
11566   }
11567
11568   closeDirectory(dir);
11569
11570   for (i = 0; i < num_sounds; i++)
11571   {
11572     sound = getSoundListEntry(i);
11573
11574     if (sound->filename == NULL)
11575       continue;
11576
11577     if (strEqual(sound->filename, UNDEFINED_FILENAME))
11578       continue;
11579
11580     // a configured file may be not recognized as sound
11581     if (!FileIsSound(sound->filename))
11582       continue;
11583
11584     if (!sound_info_listed(music_file_info, sound->filename))
11585     {
11586       *new = get_sound_file_info(sound->filename, i);
11587       if (*new != NULL)
11588         new = &(*new)->next;
11589     }
11590   }
11591 }
11592
11593 static void add_helpanim_entry(int element, int action, int direction,
11594                                int delay, int *num_list_entries)
11595 {
11596   struct HelpAnimInfo *new_list_entry;
11597   (*num_list_entries)++;
11598
11599   helpanim_info =
11600     checked_realloc(helpanim_info,
11601                     *num_list_entries * sizeof(struct HelpAnimInfo));
11602   new_list_entry = &helpanim_info[*num_list_entries - 1];
11603
11604   new_list_entry->element = element;
11605   new_list_entry->action = action;
11606   new_list_entry->direction = direction;
11607   new_list_entry->delay = delay;
11608 }
11609
11610 static void print_unknown_token(char *filename, char *token, int token_nr)
11611 {
11612   if (token_nr == 0)
11613   {
11614     Error(ERR_INFO_LINE, "-");
11615     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11616     Error(ERR_INFO, "- config file: '%s'", filename);
11617   }
11618
11619   Error(ERR_INFO, "- token: '%s'", token);
11620 }
11621
11622 static void print_unknown_token_end(int token_nr)
11623 {
11624   if (token_nr > 0)
11625     Error(ERR_INFO_LINE, "-");
11626 }
11627
11628 void LoadHelpAnimInfo(void)
11629 {
11630   char *filename = getHelpAnimFilename();
11631   SetupFileList *setup_file_list = NULL, *list;
11632   SetupFileHash *element_hash, *action_hash, *direction_hash;
11633   int num_list_entries = 0;
11634   int num_unknown_tokens = 0;
11635   int i;
11636
11637   if (fileExists(filename))
11638     setup_file_list = loadSetupFileList(filename);
11639
11640   if (setup_file_list == NULL)
11641   {
11642     // use reliable default values from static configuration
11643     SetupFileList *insert_ptr;
11644
11645     insert_ptr = setup_file_list =
11646       newSetupFileList(helpanim_config[0].token,
11647                        helpanim_config[0].value);
11648
11649     for (i = 1; helpanim_config[i].token; i++)
11650       insert_ptr = addListEntry(insert_ptr,
11651                                 helpanim_config[i].token,
11652                                 helpanim_config[i].value);
11653   }
11654
11655   element_hash   = newSetupFileHash();
11656   action_hash    = newSetupFileHash();
11657   direction_hash = newSetupFileHash();
11658
11659   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11660     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11661
11662   for (i = 0; i < NUM_ACTIONS; i++)
11663     setHashEntry(action_hash, element_action_info[i].suffix,
11664                  i_to_a(element_action_info[i].value));
11665
11666   // do not store direction index (bit) here, but direction value!
11667   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11668     setHashEntry(direction_hash, element_direction_info[i].suffix,
11669                  i_to_a(1 << element_direction_info[i].value));
11670
11671   for (list = setup_file_list; list != NULL; list = list->next)
11672   {
11673     char *element_token, *action_token, *direction_token;
11674     char *element_value, *action_value, *direction_value;
11675     int delay = atoi(list->value);
11676
11677     if (strEqual(list->token, "end"))
11678     {
11679       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11680
11681       continue;
11682     }
11683
11684     /* first try to break element into element/action/direction parts;
11685        if this does not work, also accept combined "element[.act][.dir]"
11686        elements (like "dynamite.active"), which are unique elements */
11687
11688     if (strchr(list->token, '.') == NULL)       // token contains no '.'
11689     {
11690       element_value = getHashEntry(element_hash, list->token);
11691       if (element_value != NULL)        // element found
11692         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11693                            &num_list_entries);
11694       else
11695       {
11696         // no further suffixes found -- this is not an element
11697         print_unknown_token(filename, list->token, num_unknown_tokens++);
11698       }
11699
11700       continue;
11701     }
11702
11703     // token has format "<prefix>.<something>"
11704
11705     action_token = strchr(list->token, '.');    // suffix may be action ...
11706     direction_token = action_token;             // ... or direction
11707
11708     element_token = getStringCopy(list->token);
11709     *strchr(element_token, '.') = '\0';
11710
11711     element_value = getHashEntry(element_hash, element_token);
11712
11713     if (element_value == NULL)          // this is no element
11714     {
11715       element_value = getHashEntry(element_hash, list->token);
11716       if (element_value != NULL)        // combined element found
11717         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11718                            &num_list_entries);
11719       else
11720         print_unknown_token(filename, list->token, num_unknown_tokens++);
11721
11722       free(element_token);
11723
11724       continue;
11725     }
11726
11727     action_value = getHashEntry(action_hash, action_token);
11728
11729     if (action_value != NULL)           // action found
11730     {
11731       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11732                     &num_list_entries);
11733
11734       free(element_token);
11735
11736       continue;
11737     }
11738
11739     direction_value = getHashEntry(direction_hash, direction_token);
11740
11741     if (direction_value != NULL)        // direction found
11742     {
11743       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11744                          &num_list_entries);
11745
11746       free(element_token);
11747
11748       continue;
11749     }
11750
11751     if (strchr(action_token + 1, '.') == NULL)
11752     {
11753       // no further suffixes found -- this is not an action nor direction
11754
11755       element_value = getHashEntry(element_hash, list->token);
11756       if (element_value != NULL)        // combined element found
11757         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11758                            &num_list_entries);
11759       else
11760         print_unknown_token(filename, list->token, num_unknown_tokens++);
11761
11762       free(element_token);
11763
11764       continue;
11765     }
11766
11767     // token has format "<prefix>.<suffix>.<something>"
11768
11769     direction_token = strchr(action_token + 1, '.');
11770
11771     action_token = getStringCopy(action_token);
11772     *strchr(action_token + 1, '.') = '\0';
11773
11774     action_value = getHashEntry(action_hash, action_token);
11775
11776     if (action_value == NULL)           // this is no action
11777     {
11778       element_value = getHashEntry(element_hash, list->token);
11779       if (element_value != NULL)        // combined element found
11780         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11781                            &num_list_entries);
11782       else
11783         print_unknown_token(filename, list->token, num_unknown_tokens++);
11784
11785       free(element_token);
11786       free(action_token);
11787
11788       continue;
11789     }
11790
11791     direction_value = getHashEntry(direction_hash, direction_token);
11792
11793     if (direction_value != NULL)        // direction found
11794     {
11795       add_helpanim_entry(atoi(element_value), atoi(action_value),
11796                          atoi(direction_value), delay, &num_list_entries);
11797
11798       free(element_token);
11799       free(action_token);
11800
11801       continue;
11802     }
11803
11804     // this is no direction
11805
11806     element_value = getHashEntry(element_hash, list->token);
11807     if (element_value != NULL)          // combined element found
11808       add_helpanim_entry(atoi(element_value), -1, -1, delay,
11809                          &num_list_entries);
11810     else
11811       print_unknown_token(filename, list->token, num_unknown_tokens++);
11812
11813     free(element_token);
11814     free(action_token);
11815   }
11816
11817   print_unknown_token_end(num_unknown_tokens);
11818
11819   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11820   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
11821
11822   freeSetupFileList(setup_file_list);
11823   freeSetupFileHash(element_hash);
11824   freeSetupFileHash(action_hash);
11825   freeSetupFileHash(direction_hash);
11826
11827 #if 0
11828   for (i = 0; i < num_list_entries; i++)
11829     printf("::: '%s': %d, %d, %d => %d\n",
11830            EL_NAME(helpanim_info[i].element),
11831            helpanim_info[i].element,
11832            helpanim_info[i].action,
11833            helpanim_info[i].direction,
11834            helpanim_info[i].delay);
11835 #endif
11836 }
11837
11838 void LoadHelpTextInfo(void)
11839 {
11840   char *filename = getHelpTextFilename();
11841   int i;
11842
11843   if (helptext_info != NULL)
11844   {
11845     freeSetupFileHash(helptext_info);
11846     helptext_info = NULL;
11847   }
11848
11849   if (fileExists(filename))
11850     helptext_info = loadSetupFileHash(filename);
11851
11852   if (helptext_info == NULL)
11853   {
11854     // use reliable default values from static configuration
11855     helptext_info = newSetupFileHash();
11856
11857     for (i = 0; helptext_config[i].token; i++)
11858       setHashEntry(helptext_info,
11859                    helptext_config[i].token,
11860                    helptext_config[i].value);
11861   }
11862
11863 #if 0
11864   BEGIN_HASH_ITERATION(helptext_info, itr)
11865   {
11866     printf("::: '%s' => '%s'\n",
11867            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
11868   }
11869   END_HASH_ITERATION(hash, itr)
11870 #endif
11871 }
11872
11873
11874 // ----------------------------------------------------------------------------
11875 // convert levels
11876 // ----------------------------------------------------------------------------
11877
11878 #define MAX_NUM_CONVERT_LEVELS          1000
11879
11880 void ConvertLevels(void)
11881 {
11882   static LevelDirTree *convert_leveldir = NULL;
11883   static int convert_level_nr = -1;
11884   static int num_levels_handled = 0;
11885   static int num_levels_converted = 0;
11886   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
11887   int i;
11888
11889   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
11890                                                global.convert_leveldir);
11891
11892   if (convert_leveldir == NULL)
11893     Error(ERR_EXIT, "no such level identifier: '%s'",
11894           global.convert_leveldir);
11895
11896   leveldir_current = convert_leveldir;
11897
11898   if (global.convert_level_nr != -1)
11899   {
11900     convert_leveldir->first_level = global.convert_level_nr;
11901     convert_leveldir->last_level  = global.convert_level_nr;
11902   }
11903
11904   convert_level_nr = convert_leveldir->first_level;
11905
11906   PrintLine("=", 79);
11907   Print("Converting levels\n");
11908   PrintLine("-", 79);
11909   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
11910   Print("Level series name:       '%s'\n", convert_leveldir->name);
11911   Print("Level series author:     '%s'\n", convert_leveldir->author);
11912   Print("Number of levels:        %d\n",   convert_leveldir->levels);
11913   PrintLine("=", 79);
11914   Print("\n");
11915
11916   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11917     levels_failed[i] = FALSE;
11918
11919   while (convert_level_nr <= convert_leveldir->last_level)
11920   {
11921     char *level_filename;
11922     boolean new_level;
11923
11924     level_nr = convert_level_nr++;
11925
11926     Print("Level %03d: ", level_nr);
11927
11928     LoadLevel(level_nr);
11929     if (level.no_level_file || level.no_valid_file)
11930     {
11931       Print("(no level)\n");
11932       continue;
11933     }
11934
11935     Print("converting level ... ");
11936
11937     level_filename = getDefaultLevelFilename(level_nr);
11938     new_level = !fileExists(level_filename);
11939
11940     if (new_level)
11941     {
11942       SaveLevel(level_nr);
11943
11944       num_levels_converted++;
11945
11946       Print("converted.\n");
11947     }
11948     else
11949     {
11950       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
11951         levels_failed[level_nr] = TRUE;
11952
11953       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
11954     }
11955
11956     num_levels_handled++;
11957   }
11958
11959   Print("\n");
11960   PrintLine("=", 79);
11961   Print("Number of levels handled: %d\n", num_levels_handled);
11962   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
11963          (num_levels_handled ?
11964           num_levels_converted * 100 / num_levels_handled : 0));
11965   PrintLine("-", 79);
11966   Print("Summary (for automatic parsing by scripts):\n");
11967   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
11968          convert_leveldir->identifier, num_levels_converted,
11969          num_levels_handled,
11970          (num_levels_handled ?
11971           num_levels_converted * 100 / num_levels_handled : 0));
11972
11973   if (num_levels_handled != num_levels_converted)
11974   {
11975     Print(", FAILED:");
11976     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11977       if (levels_failed[i])
11978         Print(" %03d", i);
11979   }
11980
11981   Print("\n");
11982   PrintLine("=", 79);
11983
11984   CloseAllAndExit(0);
11985 }
11986
11987
11988 // ----------------------------------------------------------------------------
11989 // create and save images for use in level sketches (raw BMP format)
11990 // ----------------------------------------------------------------------------
11991
11992 void CreateLevelSketchImages(void)
11993 {
11994   Bitmap *bitmap1;
11995   Bitmap *bitmap2;
11996   int i;
11997
11998   InitElementPropertiesGfxElement();
11999
12000   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12001   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12002
12003   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12004   {
12005     int element = getMappedElement(i);
12006     char basename1[16];
12007     char basename2[16];
12008     char *filename1;
12009     char *filename2;
12010
12011     sprintf(basename1, "%04d.bmp", i);
12012     sprintf(basename2, "%04ds.bmp", i);
12013
12014     filename1 = getPath2(global.create_images_dir, basename1);
12015     filename2 = getPath2(global.create_images_dir, basename2);
12016
12017     DrawSizedElement(0, 0, element, TILESIZE);
12018     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12019
12020     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12021       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
12022
12023     DrawSizedElement(0, 0, element, MINI_TILESIZE);
12024     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12025
12026     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12027       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
12028
12029     free(filename1);
12030     free(filename2);
12031
12032     // create corresponding SQL statements (for normal and small images)
12033     if (i < 1000)
12034     {
12035       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12036       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12037     }
12038
12039     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12040     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12041
12042     // optional: create content for forum level sketch demonstration post
12043     if (options.debug)
12044       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12045   }
12046
12047   FreeBitmap(bitmap1);
12048   FreeBitmap(bitmap2);
12049
12050   if (options.debug)
12051     fprintf(stderr, "\n");
12052
12053   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
12054
12055   CloseAllAndExit(0);
12056 }
12057
12058
12059 // ----------------------------------------------------------------------------
12060 // create and save images for custom and group elements (raw BMP format)
12061 // ----------------------------------------------------------------------------
12062
12063 void CreateCustomElementImages(char *directory)
12064 {
12065   char *src_basename = "RocksCE-template.ilbm";
12066   char *dst_basename = "RocksCE.bmp";
12067   char *src_filename = getPath2(directory, src_basename);
12068   char *dst_filename = getPath2(directory, dst_basename);
12069   Bitmap *src_bitmap;
12070   Bitmap *bitmap;
12071   int yoffset_ce = 0;
12072   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12073   int i;
12074
12075   InitVideoDefaults();
12076
12077   ReCreateBitmap(&backbuffer, video.width, video.height);
12078
12079   src_bitmap = LoadImage(src_filename);
12080
12081   bitmap = CreateBitmap(TILEX * 16 * 2,
12082                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12083                         DEFAULT_DEPTH);
12084
12085   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12086   {
12087     int x = i % 16;
12088     int y = i / 16;
12089     int ii = i + 1;
12090     int j;
12091
12092     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12093                TILEX * x, TILEY * y + yoffset_ce);
12094
12095     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12096                TILEX, TILEY,
12097                TILEX * x + TILEX * 16,
12098                TILEY * y + yoffset_ce);
12099
12100     for (j = 2; j >= 0; j--)
12101     {
12102       int c = ii % 10;
12103
12104       BlitBitmap(src_bitmap, bitmap,
12105                  TILEX + c * 7, 0, 6, 10,
12106                  TILEX * x + 6 + j * 7,
12107                  TILEY * y + 11 + yoffset_ce);
12108
12109       BlitBitmap(src_bitmap, bitmap,
12110                  TILEX + c * 8, TILEY, 6, 10,
12111                  TILEX * 16 + TILEX * x + 6 + j * 8,
12112                  TILEY * y + 10 + yoffset_ce);
12113
12114       ii /= 10;
12115     }
12116   }
12117
12118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12119   {
12120     int x = i % 16;
12121     int y = i / 16;
12122     int ii = i + 1;
12123     int j;
12124
12125     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12126                TILEX * x, TILEY * y + yoffset_ge);
12127
12128     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12129                TILEX, TILEY,
12130                TILEX * x + TILEX * 16,
12131                TILEY * y + yoffset_ge);
12132
12133     for (j = 1; j >= 0; j--)
12134     {
12135       int c = ii % 10;
12136
12137       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12138                  TILEX * x + 6 + j * 10,
12139                  TILEY * y + 11 + yoffset_ge);
12140
12141       BlitBitmap(src_bitmap, bitmap,
12142                  TILEX + c * 8, TILEY + 12, 6, 10,
12143                  TILEX * 16 + TILEX * x + 10 + j * 8,
12144                  TILEY * y + 10 + yoffset_ge);
12145
12146       ii /= 10;
12147     }
12148   }
12149
12150   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12151     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
12152
12153   FreeBitmap(bitmap);
12154
12155   CloseAllAndExit(0);
12156 }