added support for tape property bits (not used yet)
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25
26 #define ENABLE_UNUSED_CODE      0       // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
28 #define ENABLE_RESERVED_CODE    0       // reserved for later use
29
30 #define CHUNK_ID_LEN            4       // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
33
34 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
36
37 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
48
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
51
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
54 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
55
56 // (nothing at all if unchanged)
57 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
58
59 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
60 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
61 #define TAPE_CHUNK_HEAD_UNUSED  1       // unused tape header bytes
62
63 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
64 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
65 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
66
67 // file identifier strings
68 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
69 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
70 #define SCORE_COOKIE                    "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
71
72 // values for deciding when (not) to save configuration data
73 #define SAVE_CONF_NEVER                 0
74 #define SAVE_CONF_ALWAYS                1
75 #define SAVE_CONF_WHEN_CHANGED          -1
76
77 // values for chunks using micro chunks
78 #define CONF_MASK_1_BYTE                0x00
79 #define CONF_MASK_2_BYTE                0x40
80 #define CONF_MASK_4_BYTE                0x80
81 #define CONF_MASK_MULTI_BYTES           0xc0
82
83 #define CONF_MASK_BYTES                 0xc0
84 #define CONF_MASK_TOKEN                 0x3f
85
86 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
87 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
88 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
89 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
90
91 // these definitions are just for convenience of use and readability
92 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
93 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
94 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
95 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
96
97 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
98                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
99                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
100
101 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
102 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
103 #define CONF_ELEMENT_NUM_BYTES          (2)
104
105 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
106                                          (t) == TYPE_ELEMENT_LIST ?     \
107                                          CONF_ELEMENT_NUM_BYTES :       \
108                                          (t) == TYPE_CONTENT ||         \
109                                          (t) == TYPE_CONTENT_LIST ?     \
110                                          CONF_CONTENT_NUM_BYTES : 1)
111
112 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
113 #define CONF_ELEMENTS_ELEMENT(b,i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) |  \
114                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
115
116 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
117                                          (y) * 3 + (x))
118 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
119                                          CONF_ELEMENT_NUM_BYTES)
120 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
121                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
122
123 // temporary variables used to store pointers to structure members
124 static struct LevelInfo li;
125 static struct ElementInfo xx_ei, yy_ei;
126 static struct ElementChangeInfo xx_change;
127 static struct ElementGroupInfo xx_group;
128 static struct EnvelopeInfo xx_envelope;
129 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
130 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
131 static int xx_num_contents;
132 static int xx_current_change_page;
133 static char xx_default_string_empty[1] = "";
134 static int xx_string_length_unused;
135
136 struct LevelFileConfigInfo
137 {
138   int element;                  // element for which data is to be stored
139   int save_type;                // save data always, never or when changed
140   int data_type;                // data type (used internally, not stored)
141   int conf_type;                // micro chunk identifier (stored in file)
142
143   // (mandatory)
144   void *value;                  // variable that holds the data to be stored
145   int default_value;            // initial default value for this variable
146
147   // (optional)
148   void *value_copy;             // variable that holds the data to be copied
149   void *num_entities;           // number of entities for multi-byte data
150   int default_num_entities;     // default number of entities for this data
151   int max_num_entities;         // maximal number of entities for this data
152   char *default_string;         // optional default string for string data
153 };
154
155 static struct LevelFileConfigInfo chunk_config_INFO[] =
156 {
157   // ---------- values not related to single elements -------------------------
158
159   {
160     -1,                                 SAVE_CONF_ALWAYS,
161     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
162     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
163   },
164
165   {
166     -1,                                 SAVE_CONF_ALWAYS,
167     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
168     &li.fieldx,                         STD_LEV_FIELDX
169   },
170   {
171     -1,                                 SAVE_CONF_ALWAYS,
172     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
173     &li.fieldy,                         STD_LEV_FIELDY
174   },
175
176   {
177     -1,                                 SAVE_CONF_ALWAYS,
178     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
179     &li.time,                           100
180   },
181
182   {
183     -1,                                 SAVE_CONF_ALWAYS,
184     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
185     &li.gems_needed,                    0
186   },
187
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193
194   {
195     -1,                                 -1,
196     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
197     &li.use_step_counter,               FALSE
198   },
199
200   {
201     -1,                                 -1,
202     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
203     &li.wind_direction_initial,         MV_NONE
204   },
205
206   {
207     -1,                                 -1,
208     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
209     &li.em_slippery_gems,               FALSE
210   },
211
212   {
213     -1,                                 -1,
214     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
215     &li.use_custom_template,            FALSE
216   },
217
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
221     &li.can_move_into_acid_bits,        ~0      // default: everything can
222   },
223
224   {
225     -1,                                 -1,
226     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
227     &li.dont_collide_with_bits,         ~0      // default: always deadly
228   },
229
230   {
231     -1,                                 -1,
232     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
233     &li.em_explodes_by_fire,            FALSE
234   },
235
236   {
237     -1,                                 -1,
238     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
239     &li.score[SC_TIME_BONUS],           1
240   },
241
242   {
243     -1,                                 -1,
244     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
245     &li.auto_exit_sokoban,              FALSE
246   },
247
248   {
249     -1,                                 -1,
250     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
251     &li.auto_count_gems,                FALSE
252   },
253
254   {
255     -1,                                 -1,
256     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
257     &li.solved_by_one_player,           FALSE
258   },
259
260   {
261     -1,                                 -1,
262     -1,                                 -1,
263     NULL,                               -1
264   }
265 };
266
267 static struct LevelFileConfigInfo chunk_config_ELEM[] =
268 {
269   // (these values are the same for each player)
270   {
271     EL_PLAYER_1,                        -1,
272     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
273     &li.block_last_field,               FALSE   // default case for EM levels
274   },
275   {
276     EL_PLAYER_1,                        -1,
277     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
278     &li.sp_block_last_field,            TRUE    // default case for SP levels
279   },
280   {
281     EL_PLAYER_1,                        -1,
282     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
283     &li.instant_relocation,             FALSE
284   },
285   {
286     EL_PLAYER_1,                        -1,
287     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
288     &li.can_pass_to_walkable,           FALSE
289   },
290   {
291     EL_PLAYER_1,                        -1,
292     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
293     &li.block_snap_field,               TRUE
294   },
295   {
296     EL_PLAYER_1,                        -1,
297     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
298     &li.continuous_snapping,            TRUE
299   },
300   {
301     EL_PLAYER_1,                        -1,
302     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
303     &li.shifted_relocation,             FALSE
304   },
305   {
306     EL_PLAYER_1,                        -1,
307     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
308     &li.lazy_relocation,                FALSE
309   },
310
311   // (these values are different for each player)
312   {
313     EL_PLAYER_1,                        -1,
314     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
315     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
316   },
317   {
318     EL_PLAYER_1,                        -1,
319     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
320     &li.initial_player_gravity[0],      FALSE
321   },
322   {
323     EL_PLAYER_1,                        -1,
324     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
325     &li.use_start_element[0],           FALSE
326   },
327   {
328     EL_PLAYER_1,                        -1,
329     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
330     &li.start_element[0],               EL_PLAYER_1
331   },
332   {
333     EL_PLAYER_1,                        -1,
334     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
335     &li.use_artwork_element[0],         FALSE
336   },
337   {
338     EL_PLAYER_1,                        -1,
339     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
340     &li.artwork_element[0],             EL_PLAYER_1
341   },
342   {
343     EL_PLAYER_1,                        -1,
344     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
345     &li.use_explosion_element[0],       FALSE
346   },
347   {
348     EL_PLAYER_1,                        -1,
349     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
350     &li.explosion_element[0],           EL_PLAYER_1
351   },
352   {
353     EL_PLAYER_1,                        -1,
354     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
355     &li.use_initial_inventory[0],       FALSE
356   },
357   {
358     EL_PLAYER_1,                        -1,
359     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
360     &li.initial_inventory_size[0],      1
361   },
362   {
363     EL_PLAYER_1,                        -1,
364     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
365     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
366     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
367   },
368
369   {
370     EL_PLAYER_2,                        -1,
371     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
372     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
373   },
374   {
375     EL_PLAYER_2,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
377     &li.initial_player_gravity[1],      FALSE
378   },
379   {
380     EL_PLAYER_2,                        -1,
381     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
382     &li.use_start_element[1],           FALSE
383   },
384   {
385     EL_PLAYER_2,                        -1,
386     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
387     &li.start_element[1],               EL_PLAYER_2
388   },
389   {
390     EL_PLAYER_2,                        -1,
391     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
392     &li.use_artwork_element[1],         FALSE
393   },
394   {
395     EL_PLAYER_2,                        -1,
396     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
397     &li.artwork_element[1],             EL_PLAYER_2
398   },
399   {
400     EL_PLAYER_2,                        -1,
401     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
402     &li.use_explosion_element[1],       FALSE
403   },
404   {
405     EL_PLAYER_2,                        -1,
406     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
407     &li.explosion_element[1],           EL_PLAYER_2
408   },
409   {
410     EL_PLAYER_2,                        -1,
411     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
412     &li.use_initial_inventory[1],       FALSE
413   },
414   {
415     EL_PLAYER_2,                        -1,
416     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
417     &li.initial_inventory_size[1],      1
418   },
419   {
420     EL_PLAYER_2,                        -1,
421     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
422     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
423     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
424   },
425
426   {
427     EL_PLAYER_3,                        -1,
428     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
429     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
430   },
431   {
432     EL_PLAYER_3,                        -1,
433     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
434     &li.initial_player_gravity[2],      FALSE
435   },
436   {
437     EL_PLAYER_3,                        -1,
438     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
439     &li.use_start_element[2],           FALSE
440   },
441   {
442     EL_PLAYER_3,                        -1,
443     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
444     &li.start_element[2],               EL_PLAYER_3
445   },
446   {
447     EL_PLAYER_3,                        -1,
448     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
449     &li.use_artwork_element[2],         FALSE
450   },
451   {
452     EL_PLAYER_3,                        -1,
453     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
454     &li.artwork_element[2],             EL_PLAYER_3
455   },
456   {
457     EL_PLAYER_3,                        -1,
458     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
459     &li.use_explosion_element[2],       FALSE
460   },
461   {
462     EL_PLAYER_3,                        -1,
463     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
464     &li.explosion_element[2],           EL_PLAYER_3
465   },
466   {
467     EL_PLAYER_3,                        -1,
468     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
469     &li.use_initial_inventory[2],       FALSE
470   },
471   {
472     EL_PLAYER_3,                        -1,
473     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
474     &li.initial_inventory_size[2],      1
475   },
476   {
477     EL_PLAYER_3,                        -1,
478     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
479     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
480     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
481   },
482
483   {
484     EL_PLAYER_4,                        -1,
485     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
486     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
487   },
488   {
489     EL_PLAYER_4,                        -1,
490     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
491     &li.initial_player_gravity[3],      FALSE
492   },
493   {
494     EL_PLAYER_4,                        -1,
495     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
496     &li.use_start_element[3],           FALSE
497   },
498   {
499     EL_PLAYER_4,                        -1,
500     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
501     &li.start_element[3],               EL_PLAYER_4
502   },
503   {
504     EL_PLAYER_4,                        -1,
505     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
506     &li.use_artwork_element[3],         FALSE
507   },
508   {
509     EL_PLAYER_4,                        -1,
510     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
511     &li.artwork_element[3],             EL_PLAYER_4
512   },
513   {
514     EL_PLAYER_4,                        -1,
515     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
516     &li.use_explosion_element[3],       FALSE
517   },
518   {
519     EL_PLAYER_4,                        -1,
520     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
521     &li.explosion_element[3],           EL_PLAYER_4
522   },
523   {
524     EL_PLAYER_4,                        -1,
525     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
526     &li.use_initial_inventory[3],       FALSE
527   },
528   {
529     EL_PLAYER_4,                        -1,
530     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
531     &li.initial_inventory_size[3],      1
532   },
533   {
534     EL_PLAYER_4,                        -1,
535     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
536     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
537     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
538   },
539
540   {
541     EL_EMERALD,                         -1,
542     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
543     &li.score[SC_EMERALD],              10
544   },
545
546   {
547     EL_DIAMOND,                         -1,
548     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
549     &li.score[SC_DIAMOND],              10
550   },
551
552   {
553     EL_BUG,                             -1,
554     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
555     &li.score[SC_BUG],                  10
556   },
557
558   {
559     EL_SPACESHIP,                       -1,
560     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
561     &li.score[SC_SPACESHIP],            10
562   },
563
564   {
565     EL_PACMAN,                          -1,
566     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
567     &li.score[SC_PACMAN],               10
568   },
569
570   {
571     EL_NUT,                             -1,
572     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
573     &li.score[SC_NUT],                  10
574   },
575
576   {
577     EL_DYNAMITE,                        -1,
578     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
579     &li.score[SC_DYNAMITE],             10
580   },
581
582   {
583     EL_KEY_1,                           -1,
584     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
585     &li.score[SC_KEY],                  10
586   },
587
588   {
589     EL_PEARL,                           -1,
590     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
591     &li.score[SC_PEARL],                10
592   },
593
594   {
595     EL_CRYSTAL,                         -1,
596     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
597     &li.score[SC_CRYSTAL],              10
598   },
599
600   {
601     EL_BD_AMOEBA,                       -1,
602     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
603     &li.amoeba_content,                 EL_DIAMOND
604   },
605   {
606     EL_BD_AMOEBA,                       -1,
607     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
608     &li.amoeba_speed,                   10
609   },
610   {
611     EL_BD_AMOEBA,                       -1,
612     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
613     &li.grow_into_diggable,             TRUE
614   },
615
616   {
617     EL_YAMYAM,                          -1,
618     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
619     &li.yamyam_content,                 EL_ROCK, NULL,
620     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
621   },
622   {
623     EL_YAMYAM,                          -1,
624     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
625     &li.score[SC_YAMYAM],               10
626   },
627
628   {
629     EL_ROBOT,                           -1,
630     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
631     &li.score[SC_ROBOT],                10
632   },
633   {
634     EL_ROBOT,                           -1,
635     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
636     &li.slurp_score,                    10
637   },
638
639   {
640     EL_ROBOT_WHEEL,                     -1,
641     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
642     &li.time_wheel,                     10
643   },
644
645   {
646     EL_MAGIC_WALL,                      -1,
647     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
648     &li.time_magic_wall,                10
649   },
650
651   {
652     EL_GAME_OF_LIFE,                    -1,
653     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
654     &li.game_of_life[0],                2
655   },
656   {
657     EL_GAME_OF_LIFE,                    -1,
658     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
659     &li.game_of_life[1],                3
660   },
661   {
662     EL_GAME_OF_LIFE,                    -1,
663     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
664     &li.game_of_life[2],                3
665   },
666   {
667     EL_GAME_OF_LIFE,                    -1,
668     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
669     &li.game_of_life[3],                3
670   },
671   {
672     EL_GAME_OF_LIFE,                    -1,
673     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
674     &li.use_life_bugs,                  FALSE
675   },
676
677   {
678     EL_BIOMAZE,                         -1,
679     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
680     &li.biomaze[0],                     2
681   },
682   {
683     EL_BIOMAZE,                         -1,
684     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
685     &li.biomaze[1],                     3
686   },
687   {
688     EL_BIOMAZE,                         -1,
689     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
690     &li.biomaze[2],                     3
691   },
692   {
693     EL_BIOMAZE,                         -1,
694     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
695     &li.biomaze[3],                     3
696   },
697
698   {
699     EL_TIMEGATE_SWITCH,                 -1,
700     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
701     &li.time_timegate,                  10
702   },
703
704   {
705     EL_LIGHT_SWITCH_ACTIVE,             -1,
706     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
707     &li.time_light,                     10
708   },
709
710   {
711     EL_SHIELD_NORMAL,                   -1,
712     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
713     &li.shield_normal_time,             10
714   },
715   {
716     EL_SHIELD_NORMAL,                   -1,
717     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
718     &li.score[SC_SHIELD],               10
719   },
720
721   {
722     EL_SHIELD_DEADLY,                   -1,
723     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
724     &li.shield_deadly_time,             10
725   },
726   {
727     EL_SHIELD_DEADLY,                   -1,
728     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
729     &li.score[SC_SHIELD],               10
730   },
731
732   {
733     EL_EXTRA_TIME,                      -1,
734     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
735     &li.extra_time,                     10
736   },
737   {
738     EL_EXTRA_TIME,                      -1,
739     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
740     &li.extra_time_score,               10
741   },
742
743   {
744     EL_TIME_ORB_FULL,                   -1,
745     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
746     &li.time_orb_time,                  10
747   },
748   {
749     EL_TIME_ORB_FULL,                   -1,
750     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
751     &li.use_time_orb_bug,               FALSE
752   },
753
754   {
755     EL_SPRING,                          -1,
756     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
757     &li.use_spring_bug,                 FALSE
758   },
759
760   {
761     EL_EMC_ANDROID,                     -1,
762     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
763     &li.android_move_time,              10
764   },
765   {
766     EL_EMC_ANDROID,                     -1,
767     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
768     &li.android_clone_time,             10
769   },
770   {
771     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
772     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
773     &li.android_clone_element[0],       EL_EMPTY, NULL,
774     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
775   },
776   {
777     EL_EMC_ANDROID,                     -1,
778     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
779     &li.android_clone_element[0],       EL_EMPTY, NULL,
780     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
781   },
782
783   {
784     EL_EMC_LENSES,                      -1,
785     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
786     &li.lenses_score,                   10
787   },
788   {
789     EL_EMC_LENSES,                      -1,
790     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
791     &li.lenses_time,                    10
792   },
793
794   {
795     EL_EMC_MAGNIFIER,                   -1,
796     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
797     &li.magnify_score,                  10
798   },
799   {
800     EL_EMC_MAGNIFIER,                   -1,
801     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
802     &li.magnify_time,                   10
803   },
804
805   {
806     EL_EMC_MAGIC_BALL,                  -1,
807     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
808     &li.ball_time,                      10
809   },
810   {
811     EL_EMC_MAGIC_BALL,                  -1,
812     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
813     &li.ball_random,                    FALSE
814   },
815   {
816     EL_EMC_MAGIC_BALL,                  -1,
817     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
818     &li.ball_active_initial,            FALSE
819   },
820   {
821     EL_EMC_MAGIC_BALL,                  -1,
822     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
823     &li.ball_content,                   EL_EMPTY, NULL,
824     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
825   },
826
827   {
828     EL_SOKOBAN_FIELD_EMPTY,             -1,
829     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
830     &li.sb_fields_needed,               TRUE
831   },
832
833   {
834     EL_SOKOBAN_OBJECT,                  -1,
835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
836     &li.sb_objects_needed,              TRUE
837   },
838
839   {
840     EL_MM_MCDUFFIN,                     -1,
841     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
842     &li.mm_laser_red,                   FALSE
843   },
844   {
845     EL_MM_MCDUFFIN,                     -1,
846     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
847     &li.mm_laser_green,                 FALSE
848   },
849   {
850     EL_MM_MCDUFFIN,                     -1,
851     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
852     &li.mm_laser_blue,                  TRUE
853   },
854
855   {
856     EL_DF_LASER,                        -1,
857     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
858     &li.df_laser_red,                   TRUE
859   },
860   {
861     EL_DF_LASER,                        -1,
862     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
863     &li.df_laser_green,                 TRUE
864   },
865   {
866     EL_DF_LASER,                        -1,
867     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
868     &li.df_laser_blue,                  FALSE
869   },
870
871   {
872     EL_MM_FUSE_ACTIVE,                  -1,
873     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
874     &li.mm_time_fuse,                   25
875   },
876   {
877     EL_MM_BOMB,                         -1,
878     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
879     &li.mm_time_bomb,                   75
880   },
881   {
882     EL_MM_GRAY_BALL,                    -1,
883     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
884     &li.mm_time_ball,                   75
885   },
886   {
887     EL_MM_STEEL_BLOCK,                  -1,
888     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
889     &li.mm_time_block,                  75
890   },
891   {
892     EL_MM_LIGHTBALL,                    -1,
893     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
894     &li.score[SC_ELEM_BONUS],           10
895   },
896
897   // ---------- unused values -------------------------------------------------
898
899   {
900     EL_UNKNOWN,                         SAVE_CONF_NEVER,
901     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
902     &li.score[SC_UNKNOWN_15],           10
903   },
904
905   {
906     -1,                                 -1,
907     -1,                                 -1,
908     NULL,                               -1
909   }
910 };
911
912 static struct LevelFileConfigInfo chunk_config_NOTE[] =
913 {
914   {
915     -1,                                 -1,
916     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
917     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
918   },
919   {
920     -1,                                 -1,
921     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
922     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
923   },
924
925   {
926     -1,                                 -1,
927     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
928     &xx_envelope.autowrap,              FALSE
929   },
930   {
931     -1,                                 -1,
932     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
933     &xx_envelope.centered,              FALSE
934   },
935
936   {
937     -1,                                 -1,
938     TYPE_STRING,                        CONF_VALUE_BYTES(1),
939     &xx_envelope.text,                  -1, NULL,
940     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
941     &xx_default_string_empty[0]
942   },
943
944   {
945     -1,                                 -1,
946     -1,                                 -1,
947     NULL,                               -1
948   }
949 };
950
951 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
952 {
953   {
954     -1,                                 -1,
955     TYPE_STRING,                        CONF_VALUE_BYTES(1),
956     &xx_ei.description[0],              -1,
957     &yy_ei.description[0],
958     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
959     &xx_default_description[0]
960   },
961
962   {
963     -1,                                 -1,
964     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
965     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
966     &yy_ei.properties[EP_BITFIELD_BASE_NR]
967   },
968 #if ENABLE_RESERVED_CODE
969   // (reserved for later use)
970   {
971     -1,                                 -1,
972     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
973     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
974     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
975   },
976 #endif
977
978   {
979     -1,                                 -1,
980     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
981     &xx_ei.use_gfx_element,             FALSE,
982     &yy_ei.use_gfx_element
983   },
984   {
985     -1,                                 -1,
986     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
987     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
988     &yy_ei.gfx_element_initial
989   },
990
991   {
992     -1,                                 -1,
993     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
994     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
995     &yy_ei.access_direction
996   },
997
998   {
999     -1,                                 -1,
1000     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1001     &xx_ei.collect_score_initial,       10,
1002     &yy_ei.collect_score_initial
1003   },
1004   {
1005     -1,                                 -1,
1006     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1007     &xx_ei.collect_count_initial,       1,
1008     &yy_ei.collect_count_initial
1009   },
1010
1011   {
1012     -1,                                 -1,
1013     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1014     &xx_ei.ce_value_fixed_initial,      0,
1015     &yy_ei.ce_value_fixed_initial
1016   },
1017   {
1018     -1,                                 -1,
1019     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1020     &xx_ei.ce_value_random_initial,     0,
1021     &yy_ei.ce_value_random_initial
1022   },
1023   {
1024     -1,                                 -1,
1025     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1026     &xx_ei.use_last_ce_value,           FALSE,
1027     &yy_ei.use_last_ce_value
1028   },
1029
1030   {
1031     -1,                                 -1,
1032     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1033     &xx_ei.push_delay_fixed,            8,
1034     &yy_ei.push_delay_fixed
1035   },
1036   {
1037     -1,                                 -1,
1038     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1039     &xx_ei.push_delay_random,           8,
1040     &yy_ei.push_delay_random
1041   },
1042   {
1043     -1,                                 -1,
1044     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1045     &xx_ei.drop_delay_fixed,            0,
1046     &yy_ei.drop_delay_fixed
1047   },
1048   {
1049     -1,                                 -1,
1050     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1051     &xx_ei.drop_delay_random,           0,
1052     &yy_ei.drop_delay_random
1053   },
1054   {
1055     -1,                                 -1,
1056     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1057     &xx_ei.move_delay_fixed,            0,
1058     &yy_ei.move_delay_fixed
1059   },
1060   {
1061     -1,                                 -1,
1062     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1063     &xx_ei.move_delay_random,           0,
1064     &yy_ei.move_delay_random
1065   },
1066
1067   {
1068     -1,                                 -1,
1069     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1070     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1071     &yy_ei.move_pattern
1072   },
1073   {
1074     -1,                                 -1,
1075     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1076     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1077     &yy_ei.move_direction_initial
1078   },
1079   {
1080     -1,                                 -1,
1081     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1082     &xx_ei.move_stepsize,               TILEX / 8,
1083     &yy_ei.move_stepsize
1084   },
1085
1086   {
1087     -1,                                 -1,
1088     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1089     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1090     &yy_ei.move_enter_element
1091   },
1092   {
1093     -1,                                 -1,
1094     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1095     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1096     &yy_ei.move_leave_element
1097   },
1098   {
1099     -1,                                 -1,
1100     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1101     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1102     &yy_ei.move_leave_type
1103   },
1104
1105   {
1106     -1,                                 -1,
1107     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1108     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1109     &yy_ei.slippery_type
1110   },
1111
1112   {
1113     -1,                                 -1,
1114     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1115     &xx_ei.explosion_type,              EXPLODES_3X3,
1116     &yy_ei.explosion_type
1117   },
1118   {
1119     -1,                                 -1,
1120     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1121     &xx_ei.explosion_delay,             16,
1122     &yy_ei.explosion_delay
1123   },
1124   {
1125     -1,                                 -1,
1126     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1127     &xx_ei.ignition_delay,              8,
1128     &yy_ei.ignition_delay
1129   },
1130
1131   {
1132     -1,                                 -1,
1133     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1134     &xx_ei.content,                     EL_EMPTY_SPACE,
1135     &yy_ei.content,
1136     &xx_num_contents,                   1, 1
1137   },
1138
1139   // ---------- "num_change_pages" must be the last entry ---------------------
1140
1141   {
1142     -1,                                 SAVE_CONF_ALWAYS,
1143     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1144     &xx_ei.num_change_pages,            1,
1145     &yy_ei.num_change_pages
1146   },
1147
1148   {
1149     -1,                                 -1,
1150     -1,                                 -1,
1151     NULL,                               -1,
1152     NULL
1153   }
1154 };
1155
1156 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1157 {
1158   // ---------- "current_change_page" must be the first entry -----------------
1159
1160   {
1161     -1,                                 SAVE_CONF_ALWAYS,
1162     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1163     &xx_current_change_page,            -1
1164   },
1165
1166   // ---------- (the remaining entries can be in any order) -------------------
1167
1168   {
1169     -1,                                 -1,
1170     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1171     &xx_change.can_change,              FALSE
1172   },
1173
1174   {
1175     -1,                                 -1,
1176     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1177     &xx_event_bits[0],                  0
1178   },
1179   {
1180     -1,                                 -1,
1181     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1182     &xx_event_bits[1],                  0
1183   },
1184
1185   {
1186     -1,                                 -1,
1187     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1188     &xx_change.trigger_player,          CH_PLAYER_ANY
1189   },
1190   {
1191     -1,                                 -1,
1192     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1193     &xx_change.trigger_side,            CH_SIDE_ANY
1194   },
1195   {
1196     -1,                                 -1,
1197     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1198     &xx_change.trigger_page,            CH_PAGE_ANY
1199   },
1200
1201   {
1202     -1,                                 -1,
1203     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1204     &xx_change.target_element,          EL_EMPTY_SPACE
1205   },
1206
1207   {
1208     -1,                                 -1,
1209     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1210     &xx_change.delay_fixed,             0
1211   },
1212   {
1213     -1,                                 -1,
1214     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1215     &xx_change.delay_random,            0
1216   },
1217   {
1218     -1,                                 -1,
1219     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1220     &xx_change.delay_frames,            FRAMES_PER_SECOND
1221   },
1222
1223   {
1224     -1,                                 -1,
1225     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1226     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1227   },
1228
1229   {
1230     -1,                                 -1,
1231     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1232     &xx_change.explode,                 FALSE
1233   },
1234   {
1235     -1,                                 -1,
1236     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1237     &xx_change.use_target_content,      FALSE
1238   },
1239   {
1240     -1,                                 -1,
1241     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1242     &xx_change.only_if_complete,        FALSE
1243   },
1244   {
1245     -1,                                 -1,
1246     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1247     &xx_change.use_random_replace,      FALSE
1248   },
1249   {
1250     -1,                                 -1,
1251     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1252     &xx_change.random_percentage,       100
1253   },
1254   {
1255     -1,                                 -1,
1256     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1257     &xx_change.replace_when,            CP_WHEN_EMPTY
1258   },
1259
1260   {
1261     -1,                                 -1,
1262     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1263     &xx_change.has_action,              FALSE
1264   },
1265   {
1266     -1,                                 -1,
1267     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1268     &xx_change.action_type,             CA_NO_ACTION
1269   },
1270   {
1271     -1,                                 -1,
1272     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1273     &xx_change.action_mode,             CA_MODE_UNDEFINED
1274   },
1275   {
1276     -1,                                 -1,
1277     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1278     &xx_change.action_arg,              CA_ARG_UNDEFINED
1279   },
1280
1281   {
1282     -1,                                 -1,
1283     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1284     &xx_change.action_element,          EL_EMPTY_SPACE
1285   },
1286
1287   {
1288     -1,                                 -1,
1289     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1290     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1291     &xx_num_contents,                   1, 1
1292   },
1293
1294   {
1295     -1,                                 -1,
1296     -1,                                 -1,
1297     NULL,                               -1
1298   }
1299 };
1300
1301 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1302 {
1303   {
1304     -1,                                 -1,
1305     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1306     &xx_ei.description[0],              -1, NULL,
1307     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1308     &xx_default_description[0]
1309   },
1310
1311   {
1312     -1,                                 -1,
1313     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1314     &xx_ei.use_gfx_element,             FALSE
1315   },
1316   {
1317     -1,                                 -1,
1318     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1319     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1320   },
1321
1322   {
1323     -1,                                 -1,
1324     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1325     &xx_group.choice_mode,              ANIM_RANDOM
1326   },
1327
1328   {
1329     -1,                                 -1,
1330     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1331     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1332     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1333   },
1334
1335   {
1336     -1,                                 -1,
1337     -1,                                 -1,
1338     NULL,                               -1
1339   }
1340 };
1341
1342 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1343 {
1344   {
1345     EL_PLAYER_1,                        -1,
1346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1347     &li.block_snap_field,               TRUE
1348   },
1349   {
1350     EL_PLAYER_1,                        -1,
1351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1352     &li.continuous_snapping,            TRUE
1353   },
1354   {
1355     EL_PLAYER_1,                        -1,
1356     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1357     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1358   },
1359   {
1360     EL_PLAYER_1,                        -1,
1361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1362     &li.use_start_element[0],           FALSE
1363   },
1364   {
1365     EL_PLAYER_1,                        -1,
1366     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1367     &li.start_element[0],               EL_PLAYER_1
1368   },
1369   {
1370     EL_PLAYER_1,                        -1,
1371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1372     &li.use_artwork_element[0],         FALSE
1373   },
1374   {
1375     EL_PLAYER_1,                        -1,
1376     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1377     &li.artwork_element[0],             EL_PLAYER_1
1378   },
1379   {
1380     EL_PLAYER_1,                        -1,
1381     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1382     &li.use_explosion_element[0],       FALSE
1383   },
1384   {
1385     EL_PLAYER_1,                        -1,
1386     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1387     &li.explosion_element[0],           EL_PLAYER_1
1388   },
1389
1390   {
1391     -1,                                 -1,
1392     -1,                                 -1,
1393     NULL,                               -1
1394   }
1395 };
1396
1397 static struct
1398 {
1399   int filetype;
1400   char *id;
1401 }
1402 filetype_id_list[] =
1403 {
1404   { LEVEL_FILE_TYPE_RND,        "RND"   },
1405   { LEVEL_FILE_TYPE_BD,         "BD"    },
1406   { LEVEL_FILE_TYPE_EM,         "EM"    },
1407   { LEVEL_FILE_TYPE_SP,         "SP"    },
1408   { LEVEL_FILE_TYPE_DX,         "DX"    },
1409   { LEVEL_FILE_TYPE_SB,         "SB"    },
1410   { LEVEL_FILE_TYPE_DC,         "DC"    },
1411   { LEVEL_FILE_TYPE_MM,         "MM"    },
1412   { LEVEL_FILE_TYPE_MM,         "DF"    },
1413   { -1,                         NULL    },
1414 };
1415
1416
1417 // ============================================================================
1418 // level file functions
1419 // ============================================================================
1420
1421 static boolean check_special_flags(char *flag)
1422 {
1423   if (strEqual(options.special_flags, flag) ||
1424       strEqual(leveldir_current->special_flags, flag))
1425     return TRUE;
1426
1427   return FALSE;
1428 }
1429
1430 static struct DateInfo getCurrentDate(void)
1431 {
1432   time_t epoch_seconds = time(NULL);
1433   struct tm *now = localtime(&epoch_seconds);
1434   struct DateInfo date;
1435
1436   date.year  = now->tm_year + 1900;
1437   date.month = now->tm_mon  + 1;
1438   date.day   = now->tm_mday;
1439
1440   date.src   = DATE_SRC_CLOCK;
1441
1442   return date;
1443 }
1444
1445 static void resetEventFlags(struct ElementChangeInfo *change)
1446 {
1447   int i;
1448
1449   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1450     change->has_event[i] = FALSE;
1451 }
1452
1453 static void resetEventBits(void)
1454 {
1455   int i;
1456
1457   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1458     xx_event_bits[i] = 0;
1459 }
1460
1461 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1462 {
1463   int i;
1464
1465   /* important: only change event flag if corresponding event bit is set
1466      (this is because all xx_event_bits[] values are loaded separately,
1467      and all xx_event_bits[] values are set back to zero before loading
1468      another value xx_event_bits[x] (each value representing 32 flags)) */
1469
1470   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1471     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1472       change->has_event[i] = TRUE;
1473 }
1474
1475 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1476 {
1477   int i;
1478
1479   /* in contrast to the above function setEventFlagsFromEventBits(), it
1480      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1481      depending on the corresponding change->has_event[i] values here, as
1482      all xx_event_bits[] values are reset in resetEventBits() before */
1483
1484   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1485     if (change->has_event[i])
1486       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1487 }
1488
1489 static char *getDefaultElementDescription(struct ElementInfo *ei)
1490 {
1491   static char description[MAX_ELEMENT_NAME_LEN + 1];
1492   char *default_description = (ei->custom_description != NULL ?
1493                                ei->custom_description :
1494                                ei->editor_description);
1495   int i;
1496
1497   // always start with reliable default values
1498   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1499     description[i] = '\0';
1500
1501   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1502   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1503
1504   return &description[0];
1505 }
1506
1507 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1508 {
1509   char *default_description = getDefaultElementDescription(ei);
1510   int i;
1511
1512   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1513     ei->description[i] = default_description[i];
1514 }
1515
1516 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1517 {
1518   int i;
1519
1520   for (i = 0; conf[i].data_type != -1; i++)
1521   {
1522     int default_value = conf[i].default_value;
1523     int data_type = conf[i].data_type;
1524     int conf_type = conf[i].conf_type;
1525     int byte_mask = conf_type & CONF_MASK_BYTES;
1526
1527     if (byte_mask == CONF_MASK_MULTI_BYTES)
1528     {
1529       int default_num_entities = conf[i].default_num_entities;
1530       int max_num_entities = conf[i].max_num_entities;
1531
1532       *(int *)(conf[i].num_entities) = default_num_entities;
1533
1534       if (data_type == TYPE_STRING)
1535       {
1536         char *default_string = conf[i].default_string;
1537         char *string = (char *)(conf[i].value);
1538
1539         strncpy(string, default_string, max_num_entities);
1540       }
1541       else if (data_type == TYPE_ELEMENT_LIST)
1542       {
1543         int *element_array = (int *)(conf[i].value);
1544         int j;
1545
1546         for (j = 0; j < max_num_entities; j++)
1547           element_array[j] = default_value;
1548       }
1549       else if (data_type == TYPE_CONTENT_LIST)
1550       {
1551         struct Content *content = (struct Content *)(conf[i].value);
1552         int c, x, y;
1553
1554         for (c = 0; c < max_num_entities; c++)
1555           for (y = 0; y < 3; y++)
1556             for (x = 0; x < 3; x++)
1557               content[c].e[x][y] = default_value;
1558       }
1559     }
1560     else        // constant size configuration data (1, 2 or 4 bytes)
1561     {
1562       if (data_type == TYPE_BOOLEAN)
1563         *(boolean *)(conf[i].value) = default_value;
1564       else
1565         *(int *)    (conf[i].value) = default_value;
1566     }
1567   }
1568 }
1569
1570 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1571 {
1572   int i;
1573
1574   for (i = 0; conf[i].data_type != -1; i++)
1575   {
1576     int data_type = conf[i].data_type;
1577     int conf_type = conf[i].conf_type;
1578     int byte_mask = conf_type & CONF_MASK_BYTES;
1579
1580     if (byte_mask == CONF_MASK_MULTI_BYTES)
1581     {
1582       int max_num_entities = conf[i].max_num_entities;
1583
1584       if (data_type == TYPE_STRING)
1585       {
1586         char *string      = (char *)(conf[i].value);
1587         char *string_copy = (char *)(conf[i].value_copy);
1588
1589         strncpy(string_copy, string, max_num_entities);
1590       }
1591       else if (data_type == TYPE_ELEMENT_LIST)
1592       {
1593         int *element_array      = (int *)(conf[i].value);
1594         int *element_array_copy = (int *)(conf[i].value_copy);
1595         int j;
1596
1597         for (j = 0; j < max_num_entities; j++)
1598           element_array_copy[j] = element_array[j];
1599       }
1600       else if (data_type == TYPE_CONTENT_LIST)
1601       {
1602         struct Content *content      = (struct Content *)(conf[i].value);
1603         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1604         int c, x, y;
1605
1606         for (c = 0; c < max_num_entities; c++)
1607           for (y = 0; y < 3; y++)
1608             for (x = 0; x < 3; x++)
1609               content_copy[c].e[x][y] = content[c].e[x][y];
1610       }
1611     }
1612     else        // constant size configuration data (1, 2 or 4 bytes)
1613     {
1614       if (data_type == TYPE_BOOLEAN)
1615         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1616       else
1617         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
1618     }
1619   }
1620 }
1621
1622 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1623 {
1624   int i;
1625
1626   xx_ei = *ei_from;     // copy element data into temporary buffer
1627   yy_ei = *ei_to;       // copy element data into temporary buffer
1628
1629   copyConfigFromConfigList(chunk_config_CUSX_base);
1630
1631   *ei_from = xx_ei;
1632   *ei_to   = yy_ei;
1633
1634   // ---------- reinitialize and copy change pages ----------
1635
1636   ei_to->num_change_pages = ei_from->num_change_pages;
1637   ei_to->current_change_page = ei_from->current_change_page;
1638
1639   setElementChangePages(ei_to, ei_to->num_change_pages);
1640
1641   for (i = 0; i < ei_to->num_change_pages; i++)
1642     ei_to->change_page[i] = ei_from->change_page[i];
1643
1644   // ---------- copy group element info ----------
1645   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
1646     *ei_to->group = *ei_from->group;
1647
1648   // mark this custom element as modified
1649   ei_to->modified_settings = TRUE;
1650 }
1651
1652 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1653 {
1654   int change_page_size = sizeof(struct ElementChangeInfo);
1655
1656   ei->num_change_pages = MAX(1, change_pages);
1657
1658   ei->change_page =
1659     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1660
1661   if (ei->current_change_page >= ei->num_change_pages)
1662     ei->current_change_page = ei->num_change_pages - 1;
1663
1664   ei->change = &ei->change_page[ei->current_change_page];
1665 }
1666
1667 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1668 {
1669   xx_change = *change;          // copy change data into temporary buffer
1670
1671   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1672
1673   *change = xx_change;
1674
1675   resetEventFlags(change);
1676
1677   change->direct_action = 0;
1678   change->other_action = 0;
1679
1680   change->pre_change_function = NULL;
1681   change->change_function = NULL;
1682   change->post_change_function = NULL;
1683 }
1684
1685 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1686 {
1687   int i, x, y;
1688
1689   li = *level;          // copy level data into temporary buffer
1690   setConfigToDefaultsFromConfigList(chunk_config_INFO);
1691   *level = li;          // copy temporary buffer back to level data
1692
1693   setLevelInfoToDefaults_EM();
1694   setLevelInfoToDefaults_SP();
1695   setLevelInfoToDefaults_MM();
1696
1697   level->native_em_level = &native_em_level;
1698   level->native_sp_level = &native_sp_level;
1699   level->native_mm_level = &native_mm_level;
1700
1701   level->file_version = FILE_VERSION_ACTUAL;
1702   level->game_version = GAME_VERSION_ACTUAL;
1703
1704   level->creation_date = getCurrentDate();
1705
1706   level->encoding_16bit_field  = TRUE;
1707   level->encoding_16bit_yamyam = TRUE;
1708   level->encoding_16bit_amoeba = TRUE;
1709
1710   // clear level name and level author string buffers
1711   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1712     level->name[i] = '\0';
1713   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1714     level->author[i] = '\0';
1715
1716   // set level name and level author to default values
1717   strcpy(level->name, NAMELESS_LEVEL_NAME);
1718   strcpy(level->author, ANONYMOUS_NAME);
1719
1720   // set level playfield to playable default level with player and exit
1721   for (x = 0; x < MAX_LEV_FIELDX; x++)
1722     for (y = 0; y < MAX_LEV_FIELDY; y++)
1723       level->field[x][y] = EL_SAND;
1724
1725   level->field[0][0] = EL_PLAYER_1;
1726   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1727
1728   BorderElement = EL_STEELWALL;
1729
1730   // detect custom elements when loading them
1731   level->file_has_custom_elements = FALSE;
1732
1733   // set all bug compatibility flags to "false" => do not emulate this bug
1734   level->use_action_after_change_bug = FALSE;
1735
1736   if (leveldir_current)
1737   {
1738     // try to determine better author name than 'anonymous'
1739     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1740     {
1741       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1742       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1743     }
1744     else
1745     {
1746       switch (LEVELCLASS(leveldir_current))
1747       {
1748         case LEVELCLASS_TUTORIAL:
1749           strcpy(level->author, PROGRAM_AUTHOR_STRING);
1750           break;
1751
1752         case LEVELCLASS_CONTRIB:
1753           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1754           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1755           break;
1756
1757         case LEVELCLASS_PRIVATE:
1758           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1759           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1760           break;
1761
1762         default:
1763           // keep default value
1764           break;
1765       }
1766     }
1767   }
1768 }
1769
1770 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1771 {
1772   static boolean clipboard_elements_initialized = FALSE;
1773   int i;
1774
1775   InitElementPropertiesStatic();
1776
1777   li = *level;          // copy level data into temporary buffer
1778   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1779   *level = li;          // copy temporary buffer back to level data
1780
1781   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1782   {
1783     int element = i;
1784     struct ElementInfo *ei = &element_info[element];
1785
1786     // never initialize clipboard elements after the very first time
1787     // (to be able to use clipboard elements between several levels)
1788     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1789       continue;
1790
1791     if (IS_ENVELOPE(element))
1792     {
1793       int envelope_nr = element - EL_ENVELOPE_1;
1794
1795       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1796
1797       level->envelope[envelope_nr] = xx_envelope;
1798     }
1799
1800     if (IS_CUSTOM_ELEMENT(element) ||
1801         IS_GROUP_ELEMENT(element) ||
1802         IS_INTERNAL_ELEMENT(element))
1803     {
1804       xx_ei = *ei;      // copy element data into temporary buffer
1805
1806       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1807
1808       *ei = xx_ei;
1809     }
1810
1811     setElementChangePages(ei, 1);
1812     setElementChangeInfoToDefaults(ei->change);
1813
1814     if (IS_CUSTOM_ELEMENT(element) ||
1815         IS_GROUP_ELEMENT(element) ||
1816         IS_INTERNAL_ELEMENT(element))
1817     {
1818       setElementDescriptionToDefault(ei);
1819
1820       ei->modified_settings = FALSE;
1821     }
1822
1823     if (IS_CUSTOM_ELEMENT(element) ||
1824         IS_INTERNAL_ELEMENT(element))
1825     {
1826       // internal values used in level editor
1827
1828       ei->access_type = 0;
1829       ei->access_layer = 0;
1830       ei->access_protected = 0;
1831       ei->walk_to_action = 0;
1832       ei->smash_targets = 0;
1833       ei->deadliness = 0;
1834
1835       ei->can_explode_by_fire = FALSE;
1836       ei->can_explode_smashed = FALSE;
1837       ei->can_explode_impact = FALSE;
1838
1839       ei->current_change_page = 0;
1840     }
1841
1842     if (IS_GROUP_ELEMENT(element) ||
1843         IS_INTERNAL_ELEMENT(element))
1844     {
1845       struct ElementGroupInfo *group;
1846
1847       // initialize memory for list of elements in group
1848       if (ei->group == NULL)
1849         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1850
1851       group = ei->group;
1852
1853       xx_group = *group;        // copy group data into temporary buffer
1854
1855       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1856
1857       *group = xx_group;
1858     }
1859   }
1860
1861   clipboard_elements_initialized = TRUE;
1862 }
1863
1864 static void setLevelInfoToDefaults(struct LevelInfo *level,
1865                                    boolean level_info_only,
1866                                    boolean reset_file_status)
1867 {
1868   setLevelInfoToDefaults_Level(level);
1869
1870   if (!level_info_only)
1871     setLevelInfoToDefaults_Elements(level);
1872
1873   if (reset_file_status)
1874   {
1875     level->no_valid_file = FALSE;
1876     level->no_level_file = FALSE;
1877   }
1878
1879   level->changed = FALSE;
1880 }
1881
1882 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1883 {
1884   level_file_info->nr = 0;
1885   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1886   level_file_info->packed = FALSE;
1887
1888   setString(&level_file_info->basename, NULL);
1889   setString(&level_file_info->filename, NULL);
1890 }
1891
1892 int getMappedElement_SB(int, boolean);
1893
1894 static void ActivateLevelTemplate(void)
1895 {
1896   int x, y;
1897
1898   if (check_special_flags("load_xsb_to_ces"))
1899   {
1900     // fill smaller playfields with padding "beyond border wall" elements
1901     if (level.fieldx < level_template.fieldx ||
1902         level.fieldy < level_template.fieldy)
1903     {
1904       short field[level.fieldx][level.fieldy];
1905       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1906       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1907       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1908       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1909
1910       // copy old playfield (which is smaller than the visible area)
1911       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1912         field[x][y] = level.field[x][y];
1913
1914       // fill new, larger playfield with "beyond border wall" elements
1915       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1916         level.field[x][y] = getMappedElement_SB('_', TRUE);
1917
1918       // copy the old playfield to the middle of the new playfield
1919       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1920         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1921
1922       level.fieldx = new_fieldx;
1923       level.fieldy = new_fieldy;
1924     }
1925   }
1926
1927   // Currently there is no special action needed to activate the template
1928   // data, because 'element_info' property settings overwrite the original
1929   // level data, while all other variables do not change.
1930
1931   // Exception: 'from_level_template' elements in the original level playfield
1932   // are overwritten with the corresponding elements at the same position in
1933   // playfield from the level template.
1934
1935   for (x = 0; x < level.fieldx; x++)
1936     for (y = 0; y < level.fieldy; y++)
1937       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1938         level.field[x][y] = level_template.field[x][y];
1939
1940   if (check_special_flags("load_xsb_to_ces"))
1941   {
1942     struct LevelInfo level_backup = level;
1943
1944     // overwrite all individual level settings from template level settings
1945     level = level_template;
1946
1947     // restore level file info
1948     level.file_info = level_backup.file_info;
1949
1950     // restore playfield size
1951     level.fieldx = level_backup.fieldx;
1952     level.fieldy = level_backup.fieldy;
1953
1954     // restore playfield content
1955     for (x = 0; x < level.fieldx; x++)
1956       for (y = 0; y < level.fieldy; y++)
1957         level.field[x][y] = level_backup.field[x][y];
1958
1959     // restore name and author from individual level
1960     strcpy(level.name,   level_backup.name);
1961     strcpy(level.author, level_backup.author);
1962
1963     // restore flag "use_custom_template"
1964     level.use_custom_template = level_backup.use_custom_template;
1965   }
1966 }
1967
1968 static char *getLevelFilenameFromBasename(char *basename)
1969 {
1970   static char *filename = NULL;
1971
1972   checked_free(filename);
1973
1974   filename = getPath2(getCurrentLevelDir(), basename);
1975
1976   return filename;
1977 }
1978
1979 static int getFileTypeFromBasename(char *basename)
1980 {
1981   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
1982
1983   static char *filename = NULL;
1984   struct stat file_status;
1985
1986   // ---------- try to determine file type from filename ----------
1987
1988   // check for typical filename of a Supaplex level package file
1989   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1990     return LEVEL_FILE_TYPE_SP;
1991
1992   // check for typical filename of a Diamond Caves II level package file
1993   if (strSuffixLower(basename, ".dc") ||
1994       strSuffixLower(basename, ".dc2"))
1995     return LEVEL_FILE_TYPE_DC;
1996
1997   // check for typical filename of a Sokoban level package file
1998   if (strSuffixLower(basename, ".xsb") &&
1999       strchr(basename, '%') == NULL)
2000     return LEVEL_FILE_TYPE_SB;
2001
2002   // ---------- try to determine file type from filesize ----------
2003
2004   checked_free(filename);
2005   filename = getPath2(getCurrentLevelDir(), basename);
2006
2007   if (stat(filename, &file_status) == 0)
2008   {
2009     // check for typical filesize of a Supaplex level package file
2010     if (file_status.st_size == 170496)
2011       return LEVEL_FILE_TYPE_SP;
2012   }
2013
2014   return LEVEL_FILE_TYPE_UNKNOWN;
2015 }
2016
2017 static int getFileTypeFromMagicBytes(char *filename, int type)
2018 {
2019   File *file;
2020
2021   if ((file = openFile(filename, MODE_READ)))
2022   {
2023     char chunk_name[CHUNK_ID_LEN + 1];
2024
2025     getFileChunkBE(file, chunk_name, NULL);
2026
2027     if (strEqual(chunk_name, "MMII") ||
2028         strEqual(chunk_name, "MIRR"))
2029       type = LEVEL_FILE_TYPE_MM;
2030
2031     closeFile(file);
2032   }
2033
2034   return type;
2035 }
2036
2037 static boolean checkForPackageFromBasename(char *basename)
2038 {
2039   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2040   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2041
2042   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2043 }
2044
2045 static char *getSingleLevelBasenameExt(int nr, char *extension)
2046 {
2047   static char basename[MAX_FILENAME_LEN];
2048
2049   if (nr < 0)
2050     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2051   else
2052     sprintf(basename, "%03d.%s", nr, extension);
2053
2054   return basename;
2055 }
2056
2057 static char *getSingleLevelBasename(int nr)
2058 {
2059   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2060 }
2061
2062 static char *getPackedLevelBasename(int type)
2063 {
2064   static char basename[MAX_FILENAME_LEN];
2065   char *directory = getCurrentLevelDir();
2066   Directory *dir;
2067   DirectoryEntry *dir_entry;
2068
2069   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2070
2071   if ((dir = openDirectory(directory)) == NULL)
2072   {
2073     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.property_bits = TAPE_PROPERTY_NONE;
7614
7615   tape.level_nr = level_nr;
7616   tape.counter = 0;
7617   tape.changed = FALSE;
7618
7619   tape.recording = FALSE;
7620   tape.playing = FALSE;
7621   tape.pausing = FALSE;
7622
7623   tape.no_valid_file = FALSE;
7624 }
7625
7626 static int getTapePosSize(struct TapeInfo *tape)
7627 {
7628   int tape_pos_size = 0;
7629
7630   if (tape->use_key_actions)
7631     tape_pos_size += tape->num_participating_players;
7632
7633   if (tape->use_mouse_actions)
7634     tape_pos_size += 3;         // x and y position and mouse button mask
7635
7636   tape_pos_size += 1;           // tape action delay value
7637
7638   return tape_pos_size;
7639 }
7640
7641 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7642 {
7643   tape->use_key_actions = FALSE;
7644   tape->use_mouse_actions = FALSE;
7645
7646   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7647     tape->use_key_actions = TRUE;
7648
7649   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7650     tape->use_mouse_actions = TRUE;
7651 }
7652
7653 static int getTapeActionValue(struct TapeInfo *tape)
7654 {
7655   return (tape->use_key_actions &&
7656           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7657           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
7658           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7659           TAPE_ACTIONS_DEFAULT);
7660 }
7661
7662 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7663 {
7664   tape->file_version = getFileVersion(file);
7665   tape->game_version = getFileVersion(file);
7666
7667   return chunk_size;
7668 }
7669
7670 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7671 {
7672   int i;
7673
7674   tape->random_seed = getFile32BitBE(file);
7675   tape->date        = getFile32BitBE(file);
7676   tape->length      = getFile32BitBE(file);
7677
7678   // read header fields that are new since version 1.2
7679   if (tape->file_version >= FILE_VERSION_1_2)
7680   {
7681     byte store_participating_players = getFile8Bit(file);
7682     int engine_version;
7683
7684     // since version 1.2, tapes store which players participate in the tape
7685     tape->num_participating_players = 0;
7686     for (i = 0; i < MAX_PLAYERS; i++)
7687     {
7688       tape->player_participates[i] = FALSE;
7689
7690       if (store_participating_players & (1 << i))
7691       {
7692         tape->player_participates[i] = TRUE;
7693         tape->num_participating_players++;
7694       }
7695     }
7696
7697     setTapeActionFlags(tape, getFile8Bit(file));
7698
7699     tape->property_bits = getFile8Bit(file);
7700
7701     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7702
7703     engine_version = getFileVersion(file);
7704     if (engine_version > 0)
7705       tape->engine_version = engine_version;
7706     else
7707       tape->engine_version = tape->game_version;
7708   }
7709
7710   return chunk_size;
7711 }
7712
7713 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7714 {
7715   int level_identifier_size;
7716   int i;
7717
7718   level_identifier_size = getFile16BitBE(file);
7719
7720   tape->level_identifier =
7721     checked_realloc(tape->level_identifier, level_identifier_size);
7722
7723   for (i = 0; i < level_identifier_size; i++)
7724     tape->level_identifier[i] = getFile8Bit(file);
7725
7726   tape->level_nr = getFile16BitBE(file);
7727
7728   chunk_size = 2 + level_identifier_size + 2;
7729
7730   return chunk_size;
7731 }
7732
7733 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7734 {
7735   int i, j;
7736   int tape_pos_size = getTapePosSize(tape);
7737   int chunk_size_expected = tape_pos_size * tape->length;
7738
7739   if (chunk_size_expected != chunk_size)
7740   {
7741     ReadUnusedBytesFromFile(file, chunk_size);
7742     return chunk_size_expected;
7743   }
7744
7745   for (i = 0; i < tape->length; i++)
7746   {
7747     if (i >= MAX_TAPE_LEN)
7748     {
7749       Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7750             MAX_TAPE_LEN);
7751
7752       // tape too large; read and ignore remaining tape data from this chunk
7753       for (;i < tape->length; i++)
7754         ReadUnusedBytesFromFile(file, tape_pos_size);
7755
7756       break;
7757     }
7758
7759     if (tape->use_key_actions)
7760     {
7761       for (j = 0; j < MAX_PLAYERS; j++)
7762       {
7763         tape->pos[i].action[j] = MV_NONE;
7764
7765         if (tape->player_participates[j])
7766           tape->pos[i].action[j] = getFile8Bit(file);
7767       }
7768     }
7769
7770     if (tape->use_mouse_actions)
7771     {
7772       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
7773       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
7774       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7775     }
7776
7777     tape->pos[i].delay = getFile8Bit(file);
7778
7779     if (tape->file_version == FILE_VERSION_1_0)
7780     {
7781       // eliminate possible diagonal moves in old tapes
7782       // this is only for backward compatibility
7783
7784       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7785       byte action = tape->pos[i].action[0];
7786       int k, num_moves = 0;
7787
7788       for (k = 0; k<4; k++)
7789       {
7790         if (action & joy_dir[k])
7791         {
7792           tape->pos[i + num_moves].action[0] = joy_dir[k];
7793           if (num_moves > 0)
7794             tape->pos[i + num_moves].delay = 0;
7795           num_moves++;
7796         }
7797       }
7798
7799       if (num_moves > 1)
7800       {
7801         num_moves--;
7802         i += num_moves;
7803         tape->length += num_moves;
7804       }
7805     }
7806     else if (tape->file_version < FILE_VERSION_2_0)
7807     {
7808       // convert pre-2.0 tapes to new tape format
7809
7810       if (tape->pos[i].delay > 1)
7811       {
7812         // action part
7813         tape->pos[i + 1] = tape->pos[i];
7814         tape->pos[i + 1].delay = 1;
7815
7816         // delay part
7817         for (j = 0; j < MAX_PLAYERS; j++)
7818           tape->pos[i].action[j] = MV_NONE;
7819         tape->pos[i].delay--;
7820
7821         i++;
7822         tape->length++;
7823       }
7824     }
7825
7826     if (checkEndOfFile(file))
7827       break;
7828   }
7829
7830   if (i != tape->length)
7831     chunk_size = tape_pos_size * i;
7832
7833   return chunk_size;
7834 }
7835
7836 static void LoadTape_SokobanSolution(char *filename)
7837 {
7838   File *file;
7839   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7840
7841   if (!(file = openFile(filename, MODE_READ)))
7842   {
7843     tape.no_valid_file = TRUE;
7844
7845     return;
7846   }
7847
7848   while (!checkEndOfFile(file))
7849   {
7850     unsigned char c = getByteFromFile(file);
7851
7852     if (checkEndOfFile(file))
7853       break;
7854
7855     switch (c)
7856     {
7857       case 'u':
7858       case 'U':
7859         tape.pos[tape.length].action[0] = MV_UP;
7860         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7861         tape.length++;
7862         break;
7863
7864       case 'd':
7865       case 'D':
7866         tape.pos[tape.length].action[0] = MV_DOWN;
7867         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7868         tape.length++;
7869         break;
7870
7871       case 'l':
7872       case 'L':
7873         tape.pos[tape.length].action[0] = MV_LEFT;
7874         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7875         tape.length++;
7876         break;
7877
7878       case 'r':
7879       case 'R':
7880         tape.pos[tape.length].action[0] = MV_RIGHT;
7881         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7882         tape.length++;
7883         break;
7884
7885       case '\n':
7886       case '\r':
7887       case '\t':
7888       case ' ':
7889         // ignore white-space characters
7890         break;
7891
7892       default:
7893         tape.no_valid_file = TRUE;
7894
7895         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7896
7897         break;
7898     }
7899   }
7900
7901   closeFile(file);
7902
7903   if (tape.no_valid_file)
7904     return;
7905
7906   tape.length_frames  = GetTapeLengthFrames();
7907   tape.length_seconds = GetTapeLengthSeconds();
7908 }
7909
7910 void LoadTapeFromFilename(char *filename)
7911 {
7912   char cookie[MAX_LINE_LEN];
7913   char chunk_name[CHUNK_ID_LEN + 1];
7914   File *file;
7915   int chunk_size;
7916
7917   // always start with reliable default values
7918   setTapeInfoToDefaults();
7919
7920   if (strSuffix(filename, ".sln"))
7921   {
7922     LoadTape_SokobanSolution(filename);
7923
7924     return;
7925   }
7926
7927   if (!(file = openFile(filename, MODE_READ)))
7928   {
7929     tape.no_valid_file = TRUE;
7930
7931     return;
7932   }
7933
7934   getFileChunkBE(file, chunk_name, NULL);
7935   if (strEqual(chunk_name, "RND1"))
7936   {
7937     getFile32BitBE(file);               // not used
7938
7939     getFileChunkBE(file, chunk_name, NULL);
7940     if (!strEqual(chunk_name, "TAPE"))
7941     {
7942       tape.no_valid_file = TRUE;
7943
7944       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7945
7946       closeFile(file);
7947
7948       return;
7949     }
7950   }
7951   else  // check for pre-2.0 file format with cookie string
7952   {
7953     strcpy(cookie, chunk_name);
7954     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7955       cookie[4] = '\0';
7956     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7957       cookie[strlen(cookie) - 1] = '\0';
7958
7959     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7960     {
7961       tape.no_valid_file = TRUE;
7962
7963       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7964
7965       closeFile(file);
7966
7967       return;
7968     }
7969
7970     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7971     {
7972       tape.no_valid_file = TRUE;
7973
7974       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7975
7976       closeFile(file);
7977
7978       return;
7979     }
7980
7981     // pre-2.0 tape files have no game version, so use file version here
7982     tape.game_version = tape.file_version;
7983   }
7984
7985   if (tape.file_version < FILE_VERSION_1_2)
7986   {
7987     // tape files from versions before 1.2.0 without chunk structure
7988     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7989     LoadTape_BODY(file, 2 * tape.length,      &tape);
7990   }
7991   else
7992   {
7993     static struct
7994     {
7995       char *name;
7996       int size;
7997       int (*loader)(File *, int, struct TapeInfo *);
7998     }
7999     chunk_info[] =
8000     {
8001       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
8002       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
8003       { "INFO", -1,                     LoadTape_INFO },
8004       { "BODY", -1,                     LoadTape_BODY },
8005       {  NULL,  0,                      NULL }
8006     };
8007
8008     while (getFileChunkBE(file, chunk_name, &chunk_size))
8009     {
8010       int i = 0;
8011
8012       while (chunk_info[i].name != NULL &&
8013              !strEqual(chunk_name, chunk_info[i].name))
8014         i++;
8015
8016       if (chunk_info[i].name == NULL)
8017       {
8018         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
8019               chunk_name, filename);
8020         ReadUnusedBytesFromFile(file, chunk_size);
8021       }
8022       else if (chunk_info[i].size != -1 &&
8023                chunk_info[i].size != chunk_size)
8024       {
8025         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8026               chunk_size, chunk_name, filename);
8027         ReadUnusedBytesFromFile(file, chunk_size);
8028       }
8029       else
8030       {
8031         // call function to load this tape chunk
8032         int chunk_size_expected =
8033           (chunk_info[i].loader)(file, chunk_size, &tape);
8034
8035         // the size of some chunks cannot be checked before reading other
8036         // chunks first (like "HEAD" and "BODY") that contain some header
8037         // information, so check them here
8038         if (chunk_size_expected != chunk_size)
8039         {
8040           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8041                 chunk_size, chunk_name, filename);
8042         }
8043       }
8044     }
8045   }
8046
8047   closeFile(file);
8048
8049   tape.length_frames  = GetTapeLengthFrames();
8050   tape.length_seconds = GetTapeLengthSeconds();
8051
8052 #if 0
8053   printf("::: tape file version: %d\n",   tape.file_version);
8054   printf("::: tape game version: %d\n",   tape.game_version);
8055   printf("::: tape engine version: %d\n", tape.engine_version);
8056 #endif
8057 }
8058
8059 void LoadTape(int nr)
8060 {
8061   char *filename = getTapeFilename(nr);
8062
8063   LoadTapeFromFilename(filename);
8064 }
8065
8066 void LoadSolutionTape(int nr)
8067 {
8068   char *filename = getSolutionTapeFilename(nr);
8069
8070   LoadTapeFromFilename(filename);
8071
8072   if (TAPE_IS_EMPTY(tape) &&
8073       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8074       level.native_sp_level->demo.is_available)
8075     CopyNativeTape_SP_to_RND(&level);
8076 }
8077
8078 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8079 {
8080   putFileVersion(file, tape->file_version);
8081   putFileVersion(file, tape->game_version);
8082 }
8083
8084 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8085 {
8086   int i;
8087   byte store_participating_players = 0;
8088
8089   // set bits for participating players for compact storage
8090   for (i = 0; i < MAX_PLAYERS; i++)
8091     if (tape->player_participates[i])
8092       store_participating_players |= (1 << i);
8093
8094   putFile32BitBE(file, tape->random_seed);
8095   putFile32BitBE(file, tape->date);
8096   putFile32BitBE(file, tape->length);
8097
8098   putFile8Bit(file, store_participating_players);
8099
8100   putFile8Bit(file, getTapeActionValue(tape));
8101
8102   putFile8Bit(file, tape->property_bits);
8103
8104   // unused bytes not at the end here for 4-byte alignment of engine_version
8105   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8106
8107   putFileVersion(file, tape->engine_version);
8108 }
8109
8110 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8111 {
8112   int level_identifier_size = strlen(tape->level_identifier) + 1;
8113   int i;
8114
8115   putFile16BitBE(file, level_identifier_size);
8116
8117   for (i = 0; i < level_identifier_size; i++)
8118     putFile8Bit(file, tape->level_identifier[i]);
8119
8120   putFile16BitBE(file, tape->level_nr);
8121 }
8122
8123 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8124 {
8125   int i, j;
8126
8127   for (i = 0; i < tape->length; i++)
8128   {
8129     if (tape->use_key_actions)
8130     {
8131       for (j = 0; j < MAX_PLAYERS; j++)
8132         if (tape->player_participates[j])
8133           putFile8Bit(file, tape->pos[i].action[j]);
8134     }
8135
8136     if (tape->use_mouse_actions)
8137     {
8138       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8139       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8140       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8141     }
8142
8143     putFile8Bit(file, tape->pos[i].delay);
8144   }
8145 }
8146
8147 void SaveTape(int nr)
8148 {
8149   char *filename = getTapeFilename(nr);
8150   FILE *file;
8151   int tape_pos_size;
8152   int info_chunk_size;
8153   int body_chunk_size;
8154   int i;
8155
8156   InitTapeDirectory(leveldir_current->subdir);
8157
8158   if (!(file = fopen(filename, MODE_WRITE)))
8159   {
8160     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8161     return;
8162   }
8163
8164   tape.file_version = FILE_VERSION_ACTUAL;
8165   tape.game_version = GAME_VERSION_ACTUAL;
8166
8167   tape.num_participating_players = 0;
8168
8169   // count number of participating players
8170   for (i = 0; i < MAX_PLAYERS; i++)
8171     if (tape.player_participates[i])
8172       tape.num_participating_players++;
8173
8174   tape_pos_size = getTapePosSize(&tape);
8175
8176   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8177   body_chunk_size = tape_pos_size * tape.length;
8178
8179   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8180   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8181
8182   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8183   SaveTape_VERS(file, &tape);
8184
8185   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8186   SaveTape_HEAD(file, &tape);
8187
8188   putFileChunkBE(file, "INFO", info_chunk_size);
8189   SaveTape_INFO(file, &tape);
8190
8191   putFileChunkBE(file, "BODY", body_chunk_size);
8192   SaveTape_BODY(file, &tape);
8193
8194   fclose(file);
8195
8196   SetFilePermissions(filename, PERMS_PRIVATE);
8197
8198   tape.changed = FALSE;
8199 }
8200
8201 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8202                                   unsigned int req_state_added)
8203 {
8204   char *filename = getTapeFilename(nr);
8205   boolean new_tape = !fileExists(filename);
8206   boolean tape_saved = FALSE;
8207
8208   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8209   {
8210     SaveTape(nr);
8211
8212     if (new_tape)
8213       Request(msg_saved, REQ_CONFIRM | req_state_added);
8214
8215     tape_saved = TRUE;
8216   }
8217
8218   return tape_saved;
8219 }
8220
8221 boolean SaveTapeChecked(int nr)
8222 {
8223   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8224 }
8225
8226 boolean SaveTapeChecked_LevelSolved(int nr)
8227 {
8228   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8229                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
8230 }
8231
8232 void DumpTape(struct TapeInfo *tape)
8233 {
8234   int tape_frame_counter;
8235   int i, j;
8236
8237   if (tape->no_valid_file)
8238   {
8239     Error(ERR_WARN, "cannot dump -- no valid tape file found");
8240
8241     return;
8242   }
8243
8244   PrintLine("-", 79);
8245   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8246         tape->level_nr, tape->file_version, tape->game_version);
8247   Print("                  (effective engine version %08d)\n",
8248         tape->engine_version);
8249   Print("Level series identifier: '%s'\n", tape->level_identifier);
8250   PrintLine("-", 79);
8251
8252   tape_frame_counter = 0;
8253
8254   for (i = 0; i < tape->length; i++)
8255   {
8256     if (i >= MAX_TAPE_LEN)
8257       break;
8258
8259     Print("%04d: ", i);
8260
8261     for (j = 0; j < MAX_PLAYERS; j++)
8262     {
8263       if (tape->player_participates[j])
8264       {
8265         int action = tape->pos[i].action[j];
8266
8267         Print("%d:%02x ", j, action);
8268         Print("[%c%c%c%c|%c%c] - ",
8269               (action & JOY_LEFT ? '<' : ' '),
8270               (action & JOY_RIGHT ? '>' : ' '),
8271               (action & JOY_UP ? '^' : ' '),
8272               (action & JOY_DOWN ? 'v' : ' '),
8273               (action & JOY_BUTTON_1 ? '1' : ' '),
8274               (action & JOY_BUTTON_2 ? '2' : ' '));
8275       }
8276     }
8277
8278     Print("(%03d) ", tape->pos[i].delay);
8279     Print("[%05d]\n", tape_frame_counter);
8280
8281     tape_frame_counter += tape->pos[i].delay;
8282   }
8283
8284   PrintLine("-", 79);
8285 }
8286
8287
8288 // ============================================================================
8289 // score file functions
8290 // ============================================================================
8291
8292 void LoadScore(int nr)
8293 {
8294   int i;
8295   char *filename = getScoreFilename(nr);
8296   char cookie[MAX_LINE_LEN];
8297   char line[MAX_LINE_LEN];
8298   char *line_ptr;
8299   FILE *file;
8300
8301   // always start with reliable default values
8302   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8303   {
8304     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8305     highscore[i].Score = 0;
8306   }
8307
8308   if (!(file = fopen(filename, MODE_READ)))
8309     return;
8310
8311   // check file identifier
8312   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8313     cookie[0] = '\0';
8314   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8315     cookie[strlen(cookie) - 1] = '\0';
8316
8317   if (!checkCookieString(cookie, SCORE_COOKIE))
8318   {
8319     Error(ERR_WARN, "unknown format of score file '%s'", filename);
8320     fclose(file);
8321     return;
8322   }
8323
8324   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8325   {
8326     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8327       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8328     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8329       line[0] = '\0';
8330
8331     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8332       line[strlen(line) - 1] = '\0';
8333
8334     for (line_ptr = line; *line_ptr; line_ptr++)
8335     {
8336       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8337       {
8338         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8339         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8340         break;
8341       }
8342     }
8343   }
8344
8345   fclose(file);
8346 }
8347
8348 void SaveScore(int nr)
8349 {
8350   int i;
8351   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8352   char *filename = getScoreFilename(nr);
8353   FILE *file;
8354
8355   // used instead of "leveldir_current->subdir" (for network games)
8356   InitScoreDirectory(levelset.identifier);
8357
8358   if (!(file = fopen(filename, MODE_WRITE)))
8359   {
8360     Error(ERR_WARN, "cannot save score for level %d", nr);
8361     return;
8362   }
8363
8364   fprintf(file, "%s\n\n", SCORE_COOKIE);
8365
8366   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8367     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8368
8369   fclose(file);
8370
8371   SetFilePermissions(filename, permissions);
8372 }
8373
8374
8375 // ============================================================================
8376 // setup file functions
8377 // ============================================================================
8378
8379 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8380
8381
8382 static struct TokenInfo global_setup_tokens[] =
8383 {
8384   {
8385     TYPE_STRING,
8386     &setup.player_name,                         "player_name"
8387   },
8388   {
8389     TYPE_SWITCH,
8390     &setup.sound,                               "sound"
8391   },
8392   {
8393     TYPE_SWITCH,
8394     &setup.sound_loops,                         "repeating_sound_loops"
8395   },
8396   {
8397     TYPE_SWITCH,
8398     &setup.sound_music,                         "background_music"
8399   },
8400   {
8401     TYPE_SWITCH,
8402     &setup.sound_simple,                        "simple_sound_effects"
8403   },
8404   {
8405     TYPE_SWITCH,
8406     &setup.toons,                               "toons"
8407   },
8408   {
8409     TYPE_SWITCH,
8410     &setup.scroll_delay,                        "scroll_delay"
8411   },
8412   {
8413     TYPE_SWITCH,
8414     &setup.forced_scroll_delay,                 "forced_scroll_delay"
8415   },
8416   {
8417     TYPE_INTEGER,
8418     &setup.scroll_delay_value,                  "scroll_delay_value"
8419   },
8420   {
8421     TYPE_STRING,
8422     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
8423   },
8424   {
8425     TYPE_INTEGER,
8426     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
8427   },
8428   {
8429     TYPE_SWITCH,
8430     &setup.fade_screens,                        "fade_screens"
8431   },
8432   {
8433     TYPE_SWITCH,
8434     &setup.autorecord,                          "automatic_tape_recording"
8435   },
8436   {
8437     TYPE_SWITCH,
8438     &setup.show_titlescreen,                    "show_titlescreen"
8439   },
8440   {
8441     TYPE_SWITCH,
8442     &setup.quick_doors,                         "quick_doors"
8443   },
8444   {
8445     TYPE_SWITCH,
8446     &setup.team_mode,                           "team_mode"
8447   },
8448   {
8449     TYPE_SWITCH,
8450     &setup.handicap,                            "handicap"
8451   },
8452   {
8453     TYPE_SWITCH,
8454     &setup.skip_levels,                         "skip_levels"
8455   },
8456   {
8457     TYPE_SWITCH,
8458     &setup.increment_levels,                    "increment_levels"
8459   },
8460   {
8461     TYPE_SWITCH,
8462     &setup.auto_play_next_level,                "auto_play_next_level"
8463   },
8464   {
8465     TYPE_SWITCH,
8466     &setup.skip_scores_after_game,              "skip_scores_after_game"
8467   },
8468   {
8469     TYPE_SWITCH,
8470     &setup.time_limit,                          "time_limit"
8471   },
8472   {
8473     TYPE_SWITCH,
8474     &setup.fullscreen,                          "fullscreen"
8475   },
8476   {
8477     TYPE_INTEGER,
8478     &setup.window_scaling_percent,              "window_scaling_percent"
8479   },
8480   {
8481     TYPE_STRING,
8482     &setup.window_scaling_quality,              "window_scaling_quality"
8483   },
8484   {
8485     TYPE_STRING,
8486     &setup.screen_rendering_mode,               "screen_rendering_mode"
8487   },
8488   {
8489     TYPE_STRING,
8490     &setup.vsync_mode,                          "vsync_mode"
8491   },
8492   {
8493     TYPE_SWITCH,
8494     &setup.ask_on_escape,                       "ask_on_escape"
8495   },
8496   {
8497     TYPE_SWITCH,
8498     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
8499   },
8500   {
8501     TYPE_SWITCH,
8502     &setup.ask_on_game_over,                    "ask_on_game_over"
8503   },
8504   {
8505     TYPE_SWITCH,
8506     &setup.quick_switch,                        "quick_player_switch"
8507   },
8508   {
8509     TYPE_SWITCH,
8510     &setup.input_on_focus,                      "input_on_focus"
8511   },
8512   {
8513     TYPE_SWITCH,
8514     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
8515   },
8516   {
8517     TYPE_SWITCH,
8518     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
8519   },
8520   {
8521     TYPE_SWITCH,
8522     &setup.game_speed_extended,                 "game_speed_extended"
8523   },
8524   {
8525     TYPE_INTEGER,
8526     &setup.game_frame_delay,                    "game_frame_delay"
8527   },
8528   {
8529     TYPE_SWITCH,
8530     &setup.sp_show_border_elements,             "sp_show_border_elements"
8531   },
8532   {
8533     TYPE_SWITCH,
8534     &setup.small_game_graphics,                 "small_game_graphics"
8535   },
8536   {
8537     TYPE_SWITCH,
8538     &setup.show_snapshot_buttons,               "show_snapshot_buttons"
8539   },
8540   {
8541     TYPE_STRING,
8542     &setup.graphics_set,                        "graphics_set"
8543   },
8544   {
8545     TYPE_STRING,
8546     &setup.sounds_set,                          "sounds_set"
8547   },
8548   {
8549     TYPE_STRING,
8550     &setup.music_set,                           "music_set"
8551   },
8552   {
8553     TYPE_SWITCH3,
8554     &setup.override_level_graphics,             "override_level_graphics"
8555   },
8556   {
8557     TYPE_SWITCH3,
8558     &setup.override_level_sounds,               "override_level_sounds"
8559   },
8560   {
8561     TYPE_SWITCH3,
8562     &setup.override_level_music,                "override_level_music"
8563   },
8564   {
8565     TYPE_INTEGER,
8566     &setup.volume_simple,                       "volume_simple"
8567   },
8568   {
8569     TYPE_INTEGER,
8570     &setup.volume_loops,                        "volume_loops"
8571   },
8572   {
8573     TYPE_INTEGER,
8574     &setup.volume_music,                        "volume_music"
8575   },
8576   {
8577     TYPE_SWITCH,
8578     &setup.network_mode,                        "network_mode"
8579   },
8580   {
8581     TYPE_PLAYER,
8582     &setup.network_player_nr,                   "network_player"
8583   },
8584   {
8585     TYPE_STRING,
8586     &setup.network_server_hostname,             "network_server_hostname"
8587   },
8588   {
8589     TYPE_STRING,
8590     &setup.touch.control_type,                  "touch.control_type"
8591   },
8592   {
8593     TYPE_INTEGER,
8594     &setup.touch.move_distance,                 "touch.move_distance"
8595   },
8596   {
8597     TYPE_INTEGER,
8598     &setup.touch.drop_distance,                 "touch.drop_distance"
8599   },
8600   {
8601     TYPE_INTEGER,
8602     &setup.touch.transparency,                  "touch.transparency"
8603   },
8604   {
8605     TYPE_INTEGER,
8606     &setup.touch.draw_outlined,                 "touch.draw_outlined"
8607   },
8608   {
8609     TYPE_INTEGER,
8610     &setup.touch.draw_pressed,                  "touch.draw_pressed"
8611   },
8612   {
8613     TYPE_INTEGER,
8614     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
8615   },
8616   {
8617     TYPE_INTEGER,
8618     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
8619   },
8620   {
8621     TYPE_INTEGER,
8622     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
8623   },
8624   {
8625     TYPE_INTEGER,
8626     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
8627   },
8628 };
8629
8630 static struct TokenInfo auto_setup_tokens[] =
8631 {
8632   {
8633     TYPE_INTEGER,
8634     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
8635   },
8636 };
8637
8638 static struct TokenInfo editor_setup_tokens[] =
8639 {
8640   {
8641     TYPE_SWITCH,
8642     &setup.editor.el_classic,                   "editor.el_classic"
8643   },
8644   {
8645     TYPE_SWITCH,
8646     &setup.editor.el_custom,                    "editor.el_custom"
8647   },
8648   {
8649     TYPE_SWITCH,
8650     &setup.editor.el_user_defined,              "editor.el_user_defined"
8651   },
8652   {
8653     TYPE_SWITCH,
8654     &setup.editor.el_dynamic,                   "editor.el_dynamic"
8655   },
8656   {
8657     TYPE_SWITCH,
8658     &setup.editor.el_headlines,                 "editor.el_headlines"
8659   },
8660   {
8661     TYPE_SWITCH,
8662     &setup.editor.show_element_token,           "editor.show_element_token"
8663   },
8664 };
8665
8666 static struct TokenInfo editor_cascade_setup_tokens[] =
8667 {
8668   {
8669     TYPE_SWITCH,
8670     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
8671   },
8672   {
8673     TYPE_SWITCH,
8674     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
8675   },
8676   {
8677     TYPE_SWITCH,
8678     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
8679   },
8680   {
8681     TYPE_SWITCH,
8682     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
8683   },
8684   {
8685     TYPE_SWITCH,
8686     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
8687   },
8688   {
8689     TYPE_SWITCH,
8690     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
8691   },
8692   {
8693     TYPE_SWITCH,
8694     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
8695   },
8696   {
8697     TYPE_SWITCH,
8698     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
8699   },
8700   {
8701     TYPE_SWITCH,
8702     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
8703   },
8704   {
8705     TYPE_SWITCH,
8706     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
8707   },
8708   {
8709     TYPE_SWITCH,
8710     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
8711   },
8712   {
8713     TYPE_SWITCH,
8714     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
8715   },
8716   {
8717     TYPE_SWITCH,
8718     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
8719   },
8720   {
8721     TYPE_SWITCH,
8722     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
8723   },
8724   {
8725     TYPE_SWITCH,
8726     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
8727   },
8728   {
8729     TYPE_SWITCH,
8730     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
8731   },
8732   {
8733     TYPE_SWITCH,
8734     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
8735   },
8736 };
8737
8738 static struct TokenInfo shortcut_setup_tokens[] =
8739 {
8740   {
8741     TYPE_KEY_X11,
8742     &setup.shortcut.save_game,                  "shortcut.save_game"
8743   },
8744   {
8745     TYPE_KEY_X11,
8746     &setup.shortcut.load_game,                  "shortcut.load_game"
8747   },
8748   {
8749     TYPE_KEY_X11,
8750     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
8751   },
8752   {
8753     TYPE_KEY_X11,
8754     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
8755   },
8756   {
8757     TYPE_KEY_X11,
8758     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
8759   },
8760   {
8761     TYPE_KEY_X11,
8762     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
8763   },
8764   {
8765     TYPE_KEY_X11,
8766     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
8767   },
8768   {
8769     TYPE_KEY_X11,
8770     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
8771   },
8772   {
8773     TYPE_KEY_X11,
8774     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
8775   },
8776   {
8777     TYPE_KEY_X11,
8778     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
8779   },
8780   {
8781     TYPE_KEY_X11,
8782     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
8783   },
8784   {
8785     TYPE_KEY_X11,
8786     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
8787   },
8788   {
8789     TYPE_KEY_X11,
8790     &setup.shortcut.tape_record,                "shortcut.tape_record"
8791   },
8792   {
8793     TYPE_KEY_X11,
8794     &setup.shortcut.tape_play,                  "shortcut.tape_play"
8795   },
8796   {
8797     TYPE_KEY_X11,
8798     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
8799   },
8800   {
8801     TYPE_KEY_X11,
8802     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
8803   },
8804   {
8805     TYPE_KEY_X11,
8806     &setup.shortcut.sound_music,                "shortcut.sound_music"
8807   },
8808   {
8809     TYPE_KEY_X11,
8810     &setup.shortcut.snap_left,                  "shortcut.snap_left"
8811   },
8812   {
8813     TYPE_KEY_X11,
8814     &setup.shortcut.snap_right,                 "shortcut.snap_right"
8815   },
8816   {
8817     TYPE_KEY_X11,
8818     &setup.shortcut.snap_up,                    "shortcut.snap_up"
8819   },
8820   {
8821     TYPE_KEY_X11,
8822     &setup.shortcut.snap_down,                  "shortcut.snap_down"
8823   },
8824 };
8825
8826 static struct SetupInputInfo setup_input;
8827 static struct TokenInfo player_setup_tokens[] =
8828 {
8829   {
8830     TYPE_BOOLEAN,
8831     &setup_input.use_joystick,                  ".use_joystick"
8832   },
8833   {
8834     TYPE_STRING,
8835     &setup_input.joy.device_name,               ".joy.device_name"
8836   },
8837   {
8838     TYPE_INTEGER,
8839     &setup_input.joy.xleft,                     ".joy.xleft"
8840   },
8841   {
8842     TYPE_INTEGER,
8843     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
8844   },
8845   {
8846     TYPE_INTEGER,
8847     &setup_input.joy.xright,                    ".joy.xright"
8848   },
8849   {
8850     TYPE_INTEGER,
8851     &setup_input.joy.yupper,                    ".joy.yupper"
8852   },
8853   {
8854     TYPE_INTEGER,
8855     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
8856   },
8857   {
8858     TYPE_INTEGER,
8859     &setup_input.joy.ylower,                    ".joy.ylower"
8860   },
8861   {
8862     TYPE_INTEGER,
8863     &setup_input.joy.snap,                      ".joy.snap_field"
8864   },
8865   {
8866     TYPE_INTEGER,
8867     &setup_input.joy.drop,                      ".joy.place_bomb"
8868   },
8869   {
8870     TYPE_KEY_X11,
8871     &setup_input.key.left,                      ".key.move_left"
8872   },
8873   {
8874     TYPE_KEY_X11,
8875     &setup_input.key.right,                     ".key.move_right"
8876   },
8877   {
8878     TYPE_KEY_X11,
8879     &setup_input.key.up,                        ".key.move_up"
8880   },
8881   {
8882     TYPE_KEY_X11,
8883     &setup_input.key.down,                      ".key.move_down"
8884   },
8885   {
8886     TYPE_KEY_X11,
8887     &setup_input.key.snap,                      ".key.snap_field"
8888   },
8889   {
8890     TYPE_KEY_X11,
8891     &setup_input.key.drop,                      ".key.place_bomb"
8892   },
8893 };
8894
8895 static struct TokenInfo system_setup_tokens[] =
8896 {
8897   {
8898     TYPE_STRING,
8899     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
8900   },
8901   {
8902     TYPE_STRING,
8903     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
8904   },
8905   {
8906     TYPE_STRING,
8907     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
8908   },
8909   {
8910     TYPE_INTEGER,
8911     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
8912   },
8913 };
8914
8915 static struct TokenInfo internal_setup_tokens[] =
8916 {
8917   {
8918     TYPE_STRING,
8919     &setup.internal.program_title,              "program_title"
8920   },
8921   {
8922     TYPE_STRING,
8923     &setup.internal.program_version,            "program_version"
8924   },
8925   {
8926     TYPE_STRING,
8927     &setup.internal.program_author,             "program_author"
8928   },
8929   {
8930     TYPE_STRING,
8931     &setup.internal.program_email,              "program_email"
8932   },
8933   {
8934     TYPE_STRING,
8935     &setup.internal.program_website,            "program_website"
8936   },
8937   {
8938     TYPE_STRING,
8939     &setup.internal.program_copyright,          "program_copyright"
8940   },
8941   {
8942     TYPE_STRING,
8943     &setup.internal.program_company,            "program_company"
8944   },
8945   {
8946     TYPE_STRING,
8947     &setup.internal.program_icon_file,          "program_icon_file"
8948   },
8949   {
8950     TYPE_STRING,
8951     &setup.internal.default_graphics_set,       "default_graphics_set"
8952   },
8953   {
8954     TYPE_STRING,
8955     &setup.internal.default_sounds_set,         "default_sounds_set"
8956   },
8957   {
8958     TYPE_STRING,
8959     &setup.internal.default_music_set,          "default_music_set"
8960   },
8961   {
8962     TYPE_STRING,
8963     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
8964   },
8965   {
8966     TYPE_STRING,
8967     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
8968   },
8969   {
8970     TYPE_STRING,
8971     &setup.internal.fallback_music_file,        "fallback_music_file"
8972   },
8973   {
8974     TYPE_STRING,
8975     &setup.internal.default_level_series,       "default_level_series"
8976   },
8977   {
8978     TYPE_INTEGER,
8979     &setup.internal.default_window_width,       "default_window_width"
8980   },
8981   {
8982     TYPE_INTEGER,
8983     &setup.internal.default_window_height,      "default_window_height"
8984   },
8985   {
8986     TYPE_BOOLEAN,
8987     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
8988   },
8989   {
8990     TYPE_BOOLEAN,
8991     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
8992   },
8993   {
8994     TYPE_BOOLEAN,
8995     &setup.internal.create_user_levelset,       "create_user_levelset"
8996   },
8997   {
8998     TYPE_BOOLEAN,
8999     &setup.internal.menu_game,                  "menu_game"
9000   },
9001   {
9002     TYPE_BOOLEAN,
9003     &setup.internal.menu_editor,                "menu_editor"
9004   },
9005   {
9006     TYPE_BOOLEAN,
9007     &setup.internal.menu_graphics,              "menu_graphics"
9008   },
9009   {
9010     TYPE_BOOLEAN,
9011     &setup.internal.menu_sound,                 "menu_sound"
9012   },
9013   {
9014     TYPE_BOOLEAN,
9015     &setup.internal.menu_artwork,               "menu_artwork"
9016   },
9017   {
9018     TYPE_BOOLEAN,
9019     &setup.internal.menu_input,                 "menu_input"
9020   },
9021   {
9022     TYPE_BOOLEAN,
9023     &setup.internal.menu_touch,                 "menu_touch"
9024   },
9025   {
9026     TYPE_BOOLEAN,
9027     &setup.internal.menu_shortcuts,             "menu_shortcuts"
9028   },
9029   {
9030     TYPE_BOOLEAN,
9031     &setup.internal.menu_exit,                  "menu_exit"
9032   },
9033   {
9034     TYPE_BOOLEAN,
9035     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
9036   },
9037 };
9038
9039 static struct TokenInfo debug_setup_tokens[] =
9040 {
9041   {
9042     TYPE_INTEGER,
9043     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
9044   },
9045   {
9046     TYPE_INTEGER,
9047     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
9048   },
9049   {
9050     TYPE_INTEGER,
9051     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
9052   },
9053   {
9054     TYPE_INTEGER,
9055     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
9056   },
9057   {
9058     TYPE_INTEGER,
9059     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
9060   },
9061   {
9062     TYPE_INTEGER,
9063     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
9064   },
9065   {
9066     TYPE_INTEGER,
9067     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
9068   },
9069   {
9070     TYPE_INTEGER,
9071     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
9072   },
9073   {
9074     TYPE_INTEGER,
9075     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
9076   },
9077   {
9078     TYPE_INTEGER,
9079     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
9080   },
9081   {
9082     TYPE_KEY_X11,
9083     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
9084   },
9085   {
9086     TYPE_KEY_X11,
9087     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
9088   },
9089   {
9090     TYPE_KEY_X11,
9091     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
9092   },
9093   {
9094     TYPE_KEY_X11,
9095     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
9096   },
9097   {
9098     TYPE_KEY_X11,
9099     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
9100   },
9101   {
9102     TYPE_KEY_X11,
9103     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
9104   },
9105   {
9106     TYPE_KEY_X11,
9107     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
9108   },
9109   {
9110     TYPE_KEY_X11,
9111     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
9112   },
9113   {
9114     TYPE_KEY_X11,
9115     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
9116   },
9117   {
9118     TYPE_KEY_X11,
9119     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
9120   },
9121   {
9122     TYPE_BOOLEAN,
9123     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
9124   {
9125     TYPE_BOOLEAN,
9126     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
9127   },
9128   {
9129     TYPE_BOOLEAN,
9130     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
9131   },
9132 };
9133
9134 static struct TokenInfo options_setup_tokens[] =
9135 {
9136   {
9137     TYPE_BOOLEAN,
9138     &setup.options.verbose,                     "options.verbose"
9139   },
9140 };
9141
9142 static char *get_corrected_login_name(char *login_name)
9143 {
9144   // needed because player name must be a fixed length string
9145   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
9146
9147   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
9148   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
9149
9150   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         // name has been cut
9151     if (strchr(login_name_new, ' '))
9152       *strchr(login_name_new, ' ') = '\0';
9153
9154   return login_name_new;
9155 }
9156
9157 static void setSetupInfoToDefaults(struct SetupInfo *si)
9158 {
9159   int i;
9160
9161   si->player_name = get_corrected_login_name(getLoginName());
9162
9163   si->sound = TRUE;
9164   si->sound_loops = TRUE;
9165   si->sound_music = TRUE;
9166   si->sound_simple = TRUE;
9167   si->toons = TRUE;
9168   si->scroll_delay = TRUE;
9169   si->forced_scroll_delay = FALSE;
9170   si->scroll_delay_value = STD_SCROLL_DELAY;
9171   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9172   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9173   si->fade_screens = TRUE;
9174   si->autorecord = TRUE;
9175   si->show_titlescreen = TRUE;
9176   si->quick_doors = FALSE;
9177   si->team_mode = FALSE;
9178   si->handicap = TRUE;
9179   si->skip_levels = TRUE;
9180   si->increment_levels = TRUE;
9181   si->auto_play_next_level = TRUE;
9182   si->skip_scores_after_game = FALSE;
9183   si->time_limit = TRUE;
9184   si->fullscreen = FALSE;
9185   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9186   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9187   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9188   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9189   si->ask_on_escape = TRUE;
9190   si->ask_on_escape_editor = TRUE;
9191   si->ask_on_game_over = TRUE;
9192   si->quick_switch = FALSE;
9193   si->input_on_focus = FALSE;
9194   si->prefer_aga_graphics = TRUE;
9195   si->prefer_lowpass_sounds = FALSE;
9196   si->game_speed_extended = FALSE;
9197   si->game_frame_delay = GAME_FRAME_DELAY;
9198   si->sp_show_border_elements = FALSE;
9199   si->small_game_graphics = FALSE;
9200   si->show_snapshot_buttons = FALSE;
9201
9202   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9203   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
9204   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
9205
9206   si->override_level_graphics = FALSE;
9207   si->override_level_sounds = FALSE;
9208   si->override_level_music = FALSE;
9209
9210   si->volume_simple = 100;              // percent
9211   si->volume_loops = 100;               // percent
9212   si->volume_music = 100;               // percent
9213
9214   si->network_mode = FALSE;
9215   si->network_player_nr = 0;            // first player
9216   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9217
9218   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9219   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
9220   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
9221   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
9222   si->touch.draw_outlined = TRUE;
9223   si->touch.draw_pressed = TRUE;
9224
9225   for (i = 0; i < 2; i++)
9226   {
9227     char *default_grid_button[6][2] =
9228     {
9229       { "      ", "  ^^  " },
9230       { "      ", "  ^^  " },
9231       { "      ", "<<  >>" },
9232       { "      ", "<<  >>" },
9233       { "111222", "  vv  " },
9234       { "111222", "  vv  " }
9235     };
9236     int grid_xsize = DEFAULT_GRID_XSIZE(i);
9237     int grid_ysize = DEFAULT_GRID_YSIZE(i);
9238     int min_xsize = MIN(6, grid_xsize);
9239     int min_ysize = MIN(6, grid_ysize);
9240     int startx = grid_xsize - min_xsize;
9241     int starty = grid_ysize - min_ysize;
9242     int x, y;
9243
9244     // virtual buttons grid can only be set to defaults if video is initialized
9245     // (this will be repeated if virtual buttons are not loaded from setup file)
9246     if (video.initialized)
9247     {
9248       si->touch.grid_xsize[i] = grid_xsize;
9249       si->touch.grid_ysize[i] = grid_ysize;
9250     }
9251     else
9252     {
9253       si->touch.grid_xsize[i] = -1;
9254       si->touch.grid_ysize[i] = -1;
9255     }
9256
9257     for (x = 0; x < MAX_GRID_XSIZE; x++)
9258       for (y = 0; y < MAX_GRID_YSIZE; y++)
9259         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9260
9261     for (x = 0; x < min_xsize; x++)
9262       for (y = 0; y < min_ysize; y++)
9263         si->touch.grid_button[i][x][starty + y] =
9264           default_grid_button[y][0][x];
9265
9266     for (x = 0; x < min_xsize; x++)
9267       for (y = 0; y < min_ysize; y++)
9268         si->touch.grid_button[i][startx + x][starty + y] =
9269           default_grid_button[y][1][x];
9270   }
9271
9272   si->touch.grid_initialized            = video.initialized;
9273
9274   si->editor.el_boulderdash             = TRUE;
9275   si->editor.el_emerald_mine            = TRUE;
9276   si->editor.el_emerald_mine_club       = TRUE;
9277   si->editor.el_more                    = TRUE;
9278   si->editor.el_sokoban                 = TRUE;
9279   si->editor.el_supaplex                = TRUE;
9280   si->editor.el_diamond_caves           = TRUE;
9281   si->editor.el_dx_boulderdash          = TRUE;
9282
9283   si->editor.el_mirror_magic            = TRUE;
9284   si->editor.el_deflektor               = TRUE;
9285
9286   si->editor.el_chars                   = TRUE;
9287   si->editor.el_steel_chars             = TRUE;
9288
9289   si->editor.el_classic                 = TRUE;
9290   si->editor.el_custom                  = TRUE;
9291
9292   si->editor.el_user_defined            = FALSE;
9293   si->editor.el_dynamic                 = TRUE;
9294
9295   si->editor.el_headlines               = TRUE;
9296
9297   si->editor.show_element_token         = FALSE;
9298
9299   si->editor.use_template_for_new_levels = TRUE;
9300
9301   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
9302   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
9303   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
9304
9305   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
9306   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
9307   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
9308   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
9309   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9310
9311   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
9312   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
9313   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
9314   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
9315   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
9316   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
9317
9318   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
9319   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
9320   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
9321
9322   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
9323   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
9324   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
9325   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
9326
9327   for (i = 0; i < MAX_PLAYERS; i++)
9328   {
9329     si->input[i].use_joystick = FALSE;
9330     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9331     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
9332     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9333     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
9334     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
9335     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9336     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
9337     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
9338     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
9339     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
9340     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9341     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
9342     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
9343     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
9344     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
9345   }
9346
9347   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9348   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9349   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9350   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9351
9352   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
9353   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
9354   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
9355   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
9356   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
9357   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9358   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
9359
9360   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9361
9362   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9363   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
9364   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
9365
9366   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9367   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
9368   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
9369
9370   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9371   si->internal.choose_from_top_leveldir = FALSE;
9372   si->internal.show_scaling_in_title = TRUE;
9373   si->internal.create_user_levelset = TRUE;
9374
9375   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
9376   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9377
9378   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9379   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9380   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9381   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9382   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9383   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9384   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9385   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9386   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9387   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9388
9389   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9390   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9391   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9392   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9393   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9394   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9395   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9396   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9397   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9398   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9399
9400   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9401   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
9402
9403   si->debug.show_frames_per_second = FALSE;
9404
9405   si->options.verbose = FALSE;
9406
9407 #if defined(PLATFORM_ANDROID)
9408   si->fullscreen = TRUE;
9409 #endif
9410 }
9411
9412 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9413 {
9414   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9415 }
9416
9417 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9418 {
9419   si->editor_cascade.el_bd              = TRUE;
9420   si->editor_cascade.el_em              = TRUE;
9421   si->editor_cascade.el_emc             = TRUE;
9422   si->editor_cascade.el_rnd             = TRUE;
9423   si->editor_cascade.el_sb              = TRUE;
9424   si->editor_cascade.el_sp              = TRUE;
9425   si->editor_cascade.el_dc              = TRUE;
9426   si->editor_cascade.el_dx              = TRUE;
9427
9428   si->editor_cascade.el_mm              = TRUE;
9429   si->editor_cascade.el_df              = TRUE;
9430
9431   si->editor_cascade.el_chars           = FALSE;
9432   si->editor_cascade.el_steel_chars     = FALSE;
9433   si->editor_cascade.el_ce              = FALSE;
9434   si->editor_cascade.el_ge              = FALSE;
9435   si->editor_cascade.el_ref             = FALSE;
9436   si->editor_cascade.el_user            = FALSE;
9437   si->editor_cascade.el_dynamic         = FALSE;
9438 }
9439
9440 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
9441
9442 static char *getHideSetupToken(void *setup_value)
9443 {
9444   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9445
9446   if (setup_value != NULL)
9447     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9448
9449   return hide_setup_token;
9450 }
9451
9452 void setHideSetupEntry(void *setup_value)
9453 {
9454   char *hide_setup_token = getHideSetupToken(setup_value);
9455
9456   if (setup_value != NULL)
9457     setHashEntry(hide_setup_hash, hide_setup_token, "");
9458 }
9459
9460 boolean hideSetupEntry(void *setup_value)
9461 {
9462   char *hide_setup_token = getHideSetupToken(setup_value);
9463
9464   return (setup_value != NULL &&
9465           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9466 }
9467
9468 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9469                                       struct TokenInfo *token_info,
9470                                       int token_nr, char *token_text)
9471 {
9472   char *token_hide_text = getStringCat2(token_text, ".hide");
9473   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9474
9475   // set the value of this setup option in the setup option structure
9476   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9477
9478   // check if this setup option should be hidden in the setup menu
9479   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9480     setHideSetupEntry(token_info[token_nr].value);
9481 }
9482
9483 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9484                                       struct TokenInfo *token_info,
9485                                       int token_nr)
9486 {
9487   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9488                             token_info[token_nr].text);
9489 }
9490
9491 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9492 {
9493   int i, pnr;
9494
9495   if (!setup_file_hash)
9496     return;
9497
9498   if (hide_setup_hash == NULL)
9499     hide_setup_hash = newSetupFileHash();
9500
9501   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9502     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9503
9504   setup.touch.grid_initialized = TRUE;
9505   for (i = 0; i < 2; i++)
9506   {
9507     int grid_xsize = setup.touch.grid_xsize[i];
9508     int grid_ysize = setup.touch.grid_ysize[i];
9509     int x, y;
9510
9511     // if virtual buttons are not loaded from setup file, repeat initializing
9512     // virtual buttons grid with default values later when video is initialized
9513     if (grid_xsize == -1 ||
9514         grid_ysize == -1)
9515     {
9516       setup.touch.grid_initialized = FALSE;
9517
9518       continue;
9519     }
9520
9521     for (y = 0; y < grid_ysize; y++)
9522     {
9523       char token_string[MAX_LINE_LEN];
9524
9525       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9526
9527       char *value_string = getHashEntry(setup_file_hash, token_string);
9528
9529       if (value_string == NULL)
9530         continue;
9531
9532       for (x = 0; x < grid_xsize; x++)
9533       {
9534         char c = value_string[x];
9535
9536         setup.touch.grid_button[i][x][y] =
9537           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9538       }
9539     }
9540   }
9541
9542   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9543     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9544
9545   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9546     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9547
9548   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9549   {
9550     char prefix[30];
9551
9552     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9553
9554     setup_input = setup.input[pnr];
9555     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9556     {
9557       char full_token[100];
9558
9559       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9560       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9561                                 full_token);
9562     }
9563     setup.input[pnr] = setup_input;
9564   }
9565
9566   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9567     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9568
9569   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9570     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9571
9572   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9573     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9574
9575   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9576     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9577
9578   setHideRelatedSetupEntries();
9579 }
9580
9581 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9582 {
9583   int i;
9584
9585   if (!setup_file_hash)
9586     return;
9587
9588   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9589     setSetupInfo(auto_setup_tokens, i,
9590                  getHashEntry(setup_file_hash,
9591                               auto_setup_tokens[i].text));
9592 }
9593
9594 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9595 {
9596   int i;
9597
9598   if (!setup_file_hash)
9599     return;
9600
9601   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9602     setSetupInfo(editor_cascade_setup_tokens, i,
9603                  getHashEntry(setup_file_hash,
9604                               editor_cascade_setup_tokens[i].text));
9605 }
9606
9607 void LoadSetupFromFilename(char *filename)
9608 {
9609   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9610
9611   if (setup_file_hash)
9612   {
9613     decodeSetupFileHash(setup_file_hash);
9614
9615     freeSetupFileHash(setup_file_hash);
9616   }
9617   else
9618   {
9619     Error(ERR_DEBUG, "using default setup values");
9620   }
9621 }
9622
9623 static void LoadSetup_SpecialPostProcessing(void)
9624 {
9625   char *player_name_new;
9626
9627   // needed to work around problems with fixed length strings
9628   player_name_new = get_corrected_login_name(setup.player_name);
9629   free(setup.player_name);
9630   setup.player_name = player_name_new;
9631
9632   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9633   if (setup.scroll_delay == FALSE)
9634   {
9635     setup.scroll_delay_value = MIN_SCROLL_DELAY;
9636     setup.scroll_delay = TRUE;                  // now always "on"
9637   }
9638
9639   // make sure that scroll delay value stays inside valid range
9640   setup.scroll_delay_value =
9641     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9642 }
9643
9644 void LoadSetup(void)
9645 {
9646   char *filename;
9647
9648   // always start with reliable default values
9649   setSetupInfoToDefaults(&setup);
9650
9651   // try to load setup values from default setup file
9652   filename = getDefaultSetupFilename();
9653
9654   if (fileExists(filename))
9655     LoadSetupFromFilename(filename);
9656
9657   // try to load setup values from user setup file
9658   filename = getSetupFilename();
9659
9660   LoadSetupFromFilename(filename);
9661
9662   LoadSetup_SpecialPostProcessing();
9663 }
9664
9665 void LoadSetup_AutoSetup(void)
9666 {
9667   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9668   SetupFileHash *setup_file_hash = NULL;
9669
9670   // always start with reliable default values
9671   setSetupInfoToDefaults_AutoSetup(&setup);
9672
9673   setup_file_hash = loadSetupFileHash(filename);
9674
9675   if (setup_file_hash)
9676   {
9677     decodeSetupFileHash_AutoSetup(setup_file_hash);
9678
9679     freeSetupFileHash(setup_file_hash);
9680   }
9681
9682   free(filename);
9683 }
9684
9685 void LoadSetup_EditorCascade(void)
9686 {
9687   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9688   SetupFileHash *setup_file_hash = NULL;
9689
9690   // always start with reliable default values
9691   setSetupInfoToDefaults_EditorCascade(&setup);
9692
9693   setup_file_hash = loadSetupFileHash(filename);
9694
9695   if (setup_file_hash)
9696   {
9697     decodeSetupFileHash_EditorCascade(setup_file_hash);
9698
9699     freeSetupFileHash(setup_file_hash);
9700   }
9701
9702   free(filename);
9703 }
9704
9705 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9706                                            char *mapping_line)
9707 {
9708   char mapping_guid[MAX_LINE_LEN];
9709   char *mapping_start, *mapping_end;
9710
9711   // get GUID from game controller mapping line: copy complete line
9712   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9713   mapping_guid[MAX_LINE_LEN - 1] = '\0';
9714
9715   // get GUID from game controller mapping line: cut after GUID part
9716   mapping_start = strchr(mapping_guid, ',');
9717   if (mapping_start != NULL)
9718     *mapping_start = '\0';
9719
9720   // cut newline from game controller mapping line
9721   mapping_end = strchr(mapping_line, '\n');
9722   if (mapping_end != NULL)
9723     *mapping_end = '\0';
9724
9725   // add mapping entry to game controller mappings hash
9726   setHashEntry(mappings_hash, mapping_guid, mapping_line);
9727 }
9728
9729 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9730                                                  char *filename)
9731 {
9732   FILE *file;
9733
9734   if (!(file = fopen(filename, MODE_READ)))
9735   {
9736     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9737
9738     return;
9739   }
9740
9741   while (!feof(file))
9742   {
9743     char line[MAX_LINE_LEN];
9744
9745     if (!fgets(line, MAX_LINE_LEN, file))
9746       break;
9747
9748     addGameControllerMappingToHash(mappings_hash, line);
9749   }
9750
9751   fclose(file);
9752 }
9753
9754 void SaveSetup(void)
9755 {
9756   char *filename = getSetupFilename();
9757   FILE *file;
9758   int i, pnr;
9759
9760   InitUserDataDirectory();
9761
9762   if (!(file = fopen(filename, MODE_WRITE)))
9763   {
9764     Error(ERR_WARN, "cannot write setup file '%s'", filename);
9765     return;
9766   }
9767
9768   fprintFileHeader(file, SETUP_FILENAME);
9769
9770   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9771   {
9772     // just to make things nicer :)
9773     if (global_setup_tokens[i].value == &setup.sound                    ||
9774         global_setup_tokens[i].value == &setup.graphics_set             ||
9775         global_setup_tokens[i].value == &setup.volume_simple            ||
9776         global_setup_tokens[i].value == &setup.network_mode             ||
9777         global_setup_tokens[i].value == &setup.touch.control_type       ||
9778         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
9779         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9780       fprintf(file, "\n");
9781
9782     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9783   }
9784
9785   for (i = 0; i < 2; i++)
9786   {
9787     int grid_xsize = setup.touch.grid_xsize[i];
9788     int grid_ysize = setup.touch.grid_ysize[i];
9789     int x, y;
9790
9791     fprintf(file, "\n");
9792
9793     for (y = 0; y < grid_ysize; y++)
9794     {
9795       char token_string[MAX_LINE_LEN];
9796       char value_string[MAX_LINE_LEN];
9797
9798       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9799
9800       for (x = 0; x < grid_xsize; x++)
9801       {
9802         char c = setup.touch.grid_button[i][x][y];
9803
9804         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9805       }
9806
9807       value_string[grid_xsize] = '\0';
9808
9809       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9810     }
9811   }
9812
9813   fprintf(file, "\n");
9814   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9815     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9816
9817   fprintf(file, "\n");
9818   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9819     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9820
9821   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9822   {
9823     char prefix[30];
9824
9825     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9826     fprintf(file, "\n");
9827
9828     setup_input = setup.input[pnr];
9829     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9830       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9831   }
9832
9833   fprintf(file, "\n");
9834   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9835     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9836
9837   // (internal setup values not saved to user setup file)
9838
9839   fprintf(file, "\n");
9840   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9841     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9842
9843   fprintf(file, "\n");
9844   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9845     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9846
9847   fclose(file);
9848
9849   SetFilePermissions(filename, PERMS_PRIVATE);
9850 }
9851
9852 void SaveSetup_AutoSetup(void)
9853 {
9854   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9855   FILE *file;
9856   int i;
9857
9858   InitUserDataDirectory();
9859
9860   if (!(file = fopen(filename, MODE_WRITE)))
9861   {
9862     Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9863     free(filename);
9864     return;
9865   }
9866
9867   fprintFileHeader(file, AUTOSETUP_FILENAME);
9868
9869   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9870     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9871
9872   fclose(file);
9873
9874   SetFilePermissions(filename, PERMS_PRIVATE);
9875
9876   free(filename);
9877 }
9878
9879 void SaveSetup_EditorCascade(void)
9880 {
9881   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9882   FILE *file;
9883   int i;
9884
9885   InitUserDataDirectory();
9886
9887   if (!(file = fopen(filename, MODE_WRITE)))
9888   {
9889     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9890     free(filename);
9891     return;
9892   }
9893
9894   fprintFileHeader(file, EDITORCASCADE_FILENAME);
9895
9896   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9897     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9898
9899   fclose(file);
9900
9901   SetFilePermissions(filename, PERMS_PRIVATE);
9902
9903   free(filename);
9904 }
9905
9906 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9907                                                   char *filename)
9908 {
9909   FILE *file;
9910
9911   if (!(file = fopen(filename, MODE_WRITE)))
9912   {
9913     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9914
9915     return;
9916   }
9917
9918   BEGIN_HASH_ITERATION(mappings_hash, itr)
9919   {
9920     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9921   }
9922   END_HASH_ITERATION(mappings_hash, itr)
9923
9924   fclose(file);
9925 }
9926
9927 void SaveSetup_AddGameControllerMapping(char *mapping)
9928 {
9929   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9930   SetupFileHash *mappings_hash = newSetupFileHash();
9931
9932   InitUserDataDirectory();
9933
9934   // load existing personal game controller mappings
9935   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9936
9937   // add new mapping to personal game controller mappings
9938   addGameControllerMappingToHash(mappings_hash, mapping);
9939
9940   // save updated personal game controller mappings
9941   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9942
9943   freeSetupFileHash(mappings_hash);
9944   free(filename);
9945 }
9946
9947 void LoadCustomElementDescriptions(void)
9948 {
9949   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9950   SetupFileHash *setup_file_hash;
9951   int i;
9952
9953   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9954   {
9955     if (element_info[i].custom_description != NULL)
9956     {
9957       free(element_info[i].custom_description);
9958       element_info[i].custom_description = NULL;
9959     }
9960   }
9961
9962   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9963     return;
9964
9965   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9966   {
9967     char *token = getStringCat2(element_info[i].token_name, ".name");
9968     char *value = getHashEntry(setup_file_hash, token);
9969
9970     if (value != NULL)
9971       element_info[i].custom_description = getStringCopy(value);
9972
9973     free(token);
9974   }
9975
9976   freeSetupFileHash(setup_file_hash);
9977 }
9978
9979 static int getElementFromToken(char *token)
9980 {
9981   char *value = getHashEntry(element_token_hash, token);
9982
9983   if (value != NULL)
9984     return atoi(value);
9985
9986   Error(ERR_WARN, "unknown element token '%s'", token);
9987
9988   return EL_UNDEFINED;
9989 }
9990
9991 void FreeGlobalAnimEventInfo(void)
9992 {
9993   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9994
9995   if (gaei->event_list == NULL)
9996     return;
9997
9998   int i;
9999
10000   for (i = 0; i < gaei->num_event_lists; i++)
10001   {
10002     checked_free(gaei->event_list[i]->event_value);
10003     checked_free(gaei->event_list[i]);
10004   }
10005
10006   checked_free(gaei->event_list);
10007
10008   gaei->event_list = NULL;
10009   gaei->num_event_lists = 0;
10010 }
10011
10012 static int AddGlobalAnimEventList(void)
10013 {
10014   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10015   int list_pos = gaei->num_event_lists++;
10016
10017   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10018                                      sizeof(struct GlobalAnimEventListInfo *));
10019
10020   gaei->event_list[list_pos] =
10021     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10022
10023   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10024
10025   gaeli->event_value = NULL;
10026   gaeli->num_event_values = 0;
10027
10028   return list_pos;
10029 }
10030
10031 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10032 {
10033   // do not add empty global animation events
10034   if (event_value == ANIM_EVENT_NONE)
10035     return list_pos;
10036
10037   // if list position is undefined, create new list
10038   if (list_pos == ANIM_EVENT_UNDEFINED)
10039     list_pos = AddGlobalAnimEventList();
10040
10041   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10042   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10043   int value_pos = gaeli->num_event_values++;
10044
10045   gaeli->event_value = checked_realloc(gaeli->event_value,
10046                                        gaeli->num_event_values * sizeof(int *));
10047
10048   gaeli->event_value[value_pos] = event_value;
10049
10050   return list_pos;
10051 }
10052
10053 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10054 {
10055   if (list_pos == ANIM_EVENT_UNDEFINED)
10056     return ANIM_EVENT_NONE;
10057
10058   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10059   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10060
10061   return gaeli->event_value[value_pos];
10062 }
10063
10064 int GetGlobalAnimEventValueCount(int list_pos)
10065 {
10066   if (list_pos == ANIM_EVENT_UNDEFINED)
10067     return 0;
10068
10069   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10070   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10071
10072   return gaeli->num_event_values;
10073 }
10074
10075 // This function checks if a string <s> of the format "string1, string2, ..."
10076 // exactly contains a string <s_contained>.
10077
10078 static boolean string_has_parameter(char *s, char *s_contained)
10079 {
10080   char *substring;
10081
10082   if (s == NULL || s_contained == NULL)
10083     return FALSE;
10084
10085   if (strlen(s_contained) > strlen(s))
10086     return FALSE;
10087
10088   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10089   {
10090     char next_char = s[strlen(s_contained)];
10091
10092     // check if next character is delimiter or whitespace
10093     return (next_char == ',' || next_char == '\0' ||
10094             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10095   }
10096
10097   // check if string contains another parameter string after a comma
10098   substring = strchr(s, ',');
10099   if (substring == NULL)        // string does not contain a comma
10100     return FALSE;
10101
10102   // advance string pointer to next character after the comma
10103   substring++;
10104
10105   // skip potential whitespaces after the comma
10106   while (*substring == ' ' || *substring == '\t')
10107     substring++;
10108
10109   return string_has_parameter(substring, s_contained);
10110 }
10111
10112 static int get_anim_parameter_value(char *s)
10113 {
10114   int event_value[] =
10115   {
10116     ANIM_EVENT_CLICK,
10117     ANIM_EVENT_INIT,
10118     ANIM_EVENT_START,
10119     ANIM_EVENT_END,
10120     ANIM_EVENT_POST
10121   };
10122   char *pattern_1[] =
10123   {
10124     "click:anim_",
10125     "init:anim_",
10126     "start:anim_",
10127     "end:anim_",
10128     "post:anim_"
10129   };
10130   char *pattern_2 = ".part_";
10131   char *matching_char = NULL;
10132   char *s_ptr = s;
10133   int pattern_1_len = 0;
10134   int result = ANIM_EVENT_NONE;
10135   int i;
10136
10137   for (i = 0; i < ARRAY_SIZE(event_value); i++)
10138   {
10139     matching_char = strstr(s_ptr, pattern_1[i]);
10140     pattern_1_len = strlen(pattern_1[i]);
10141     result = event_value[i];
10142
10143     if (matching_char != NULL)
10144       break;
10145   }
10146
10147   if (matching_char == NULL)
10148     return ANIM_EVENT_NONE;
10149
10150   s_ptr = matching_char + pattern_1_len;
10151
10152   // check for main animation number ("anim_X" or "anim_XX")
10153   if (*s_ptr >= '0' && *s_ptr <= '9')
10154   {
10155     int gic_anim_nr = (*s_ptr++ - '0');
10156
10157     if (*s_ptr >= '0' && *s_ptr <= '9')
10158       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10159
10160     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10161       return ANIM_EVENT_NONE;
10162
10163     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10164   }
10165   else
10166   {
10167     // invalid main animation number specified
10168
10169     return ANIM_EVENT_NONE;
10170   }
10171
10172   // check for animation part number ("part_X" or "part_XX") (optional)
10173   if (strPrefix(s_ptr, pattern_2))
10174   {
10175     s_ptr += strlen(pattern_2);
10176
10177     if (*s_ptr >= '0' && *s_ptr <= '9')
10178     {
10179       int gic_part_nr = (*s_ptr++ - '0');
10180
10181       if (*s_ptr >= '0' && *s_ptr <= '9')
10182         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10183
10184       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10185         return ANIM_EVENT_NONE;
10186
10187       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10188     }
10189     else
10190     {
10191       // invalid animation part number specified
10192
10193       return ANIM_EVENT_NONE;
10194     }
10195   }
10196
10197   // discard result if next character is neither delimiter nor whitespace
10198   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10199         *s_ptr == ' ' || *s_ptr == '\t'))
10200     return ANIM_EVENT_NONE;
10201
10202   return result;
10203 }
10204
10205 static int get_anim_parameter_values(char *s)
10206 {
10207   int list_pos = ANIM_EVENT_UNDEFINED;
10208   int event_value = ANIM_EVENT_DEFAULT;
10209
10210   if (string_has_parameter(s, "any"))
10211     event_value |= ANIM_EVENT_ANY;
10212
10213   if (string_has_parameter(s, "click:self") ||
10214       string_has_parameter(s, "click") ||
10215       string_has_parameter(s, "self"))
10216     event_value |= ANIM_EVENT_SELF;
10217
10218   if (string_has_parameter(s, "unclick:any"))
10219     event_value |= ANIM_EVENT_UNCLICK_ANY;
10220
10221   // if animation event found, add it to global animation event list
10222   if (event_value != ANIM_EVENT_NONE)
10223     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10224
10225   while (s != NULL)
10226   {
10227     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10228     event_value = get_anim_parameter_value(s);
10229
10230     // if animation event found, add it to global animation event list
10231     if (event_value != ANIM_EVENT_NONE)
10232       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10233
10234     // continue with next part of the string, starting with next comma
10235     s = strchr(s + 1, ',');
10236   }
10237
10238   return list_pos;
10239 }
10240
10241 static int get_anim_action_parameter_value(char *token)
10242 {
10243   int result = getImageIDFromToken(token);
10244
10245   if (result == -1)
10246   {
10247     char *gfx_token = getStringCat2("gfx.", token);
10248
10249     result = getImageIDFromToken(gfx_token);
10250
10251     checked_free(gfx_token);
10252   }
10253
10254   if (result == -1)
10255   {
10256     Key key = getKeyFromX11KeyName(token);
10257
10258     if (key != KSYM_UNDEFINED)
10259       result = -(int)key;
10260   }
10261
10262   if (result == -1)
10263     result = ANIM_EVENT_ACTION_NONE;
10264
10265   return result;
10266 }
10267
10268 int get_parameter_value(char *value_raw, char *suffix, int type)
10269 {
10270   char *value = getStringToLower(value_raw);
10271   int result = 0;       // probably a save default value
10272
10273   if (strEqual(suffix, ".direction"))
10274   {
10275     result = (strEqual(value, "left")  ? MV_LEFT :
10276               strEqual(value, "right") ? MV_RIGHT :
10277               strEqual(value, "up")    ? MV_UP :
10278               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
10279   }
10280   else if (strEqual(suffix, ".position"))
10281   {
10282     result = (strEqual(value, "left")   ? POS_LEFT :
10283               strEqual(value, "right")  ? POS_RIGHT :
10284               strEqual(value, "top")    ? POS_TOP :
10285               strEqual(value, "upper")  ? POS_UPPER :
10286               strEqual(value, "middle") ? POS_MIDDLE :
10287               strEqual(value, "lower")  ? POS_LOWER :
10288               strEqual(value, "bottom") ? POS_BOTTOM :
10289               strEqual(value, "any")    ? POS_ANY :
10290               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
10291   }
10292   else if (strEqual(suffix, ".align"))
10293   {
10294     result = (strEqual(value, "left")   ? ALIGN_LEFT :
10295               strEqual(value, "right")  ? ALIGN_RIGHT :
10296               strEqual(value, "center") ? ALIGN_CENTER :
10297               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10298   }
10299   else if (strEqual(suffix, ".valign"))
10300   {
10301     result = (strEqual(value, "top")    ? VALIGN_TOP :
10302               strEqual(value, "bottom") ? VALIGN_BOTTOM :
10303               strEqual(value, "middle") ? VALIGN_MIDDLE :
10304               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10305   }
10306   else if (strEqual(suffix, ".anim_mode"))
10307   {
10308     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
10309               string_has_parameter(value, "loop")       ? ANIM_LOOP :
10310               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
10311               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
10312               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
10313               string_has_parameter(value, "random")     ? ANIM_RANDOM :
10314               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
10315               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
10316               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
10317               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10318               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
10319               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
10320               string_has_parameter(value, "all")        ? ANIM_ALL :
10321               ANIM_DEFAULT);
10322
10323     if (string_has_parameter(value, "once"))
10324       result |= ANIM_ONCE;
10325
10326     if (string_has_parameter(value, "reverse"))
10327       result |= ANIM_REVERSE;
10328
10329     if (string_has_parameter(value, "opaque_player"))
10330       result |= ANIM_OPAQUE_PLAYER;
10331
10332     if (string_has_parameter(value, "static_panel"))
10333       result |= ANIM_STATIC_PANEL;
10334   }
10335   else if (strEqual(suffix, ".init_event") ||
10336            strEqual(suffix, ".anim_event"))
10337   {
10338     result = get_anim_parameter_values(value);
10339   }
10340   else if (strEqual(suffix, ".init_delay_action") ||
10341            strEqual(suffix, ".anim_delay_action") ||
10342            strEqual(suffix, ".post_delay_action") ||
10343            strEqual(suffix, ".init_event_action") ||
10344            strEqual(suffix, ".anim_event_action"))
10345   {
10346     result = get_anim_action_parameter_value(value_raw);
10347   }
10348   else if (strEqual(suffix, ".class"))
10349   {
10350     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10351               get_hash_from_key(value));
10352   }
10353   else if (strEqual(suffix, ".style"))
10354   {
10355     result = STYLE_DEFAULT;
10356
10357     if (string_has_parameter(value, "accurate_borders"))
10358       result |= STYLE_ACCURATE_BORDERS;
10359
10360     if (string_has_parameter(value, "inner_corners"))
10361       result |= STYLE_INNER_CORNERS;
10362
10363     if (string_has_parameter(value, "reverse"))
10364       result |= STYLE_REVERSE;
10365
10366     if (string_has_parameter(value, "block_clicks"))
10367       result |= STYLE_BLOCK;
10368
10369     if (string_has_parameter(value, "passthrough_clicks"))
10370       result |= STYLE_PASSTHROUGH;
10371
10372     if (string_has_parameter(value, "multiple_actions"))
10373       result |= STYLE_MULTIPLE_ACTIONS;
10374   }
10375   else if (strEqual(suffix, ".fade_mode"))
10376   {
10377     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
10378               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
10379               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
10380               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
10381               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
10382               FADE_MODE_DEFAULT);
10383   }
10384   else if (strEqual(suffix, ".auto_delay_unit"))
10385   {
10386     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
10387               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10388               AUTO_DELAY_UNIT_DEFAULT);
10389   }
10390   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
10391   {
10392     result = gfx.get_font_from_token_function(value);
10393   }
10394   else          // generic parameter of type integer or boolean
10395   {
10396     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10397               type == TYPE_INTEGER ? get_integer_from_string(value) :
10398               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10399               ARG_UNDEFINED_VALUE);
10400   }
10401
10402   free(value);
10403
10404   return result;
10405 }
10406
10407 static int get_token_parameter_value(char *token, char *value_raw)
10408 {
10409   char *suffix;
10410
10411   if (token == NULL || value_raw == NULL)
10412     return ARG_UNDEFINED_VALUE;
10413
10414   suffix = strrchr(token, '.');
10415   if (suffix == NULL)
10416     suffix = token;
10417
10418   if (strEqual(suffix, ".element"))
10419     return getElementFromToken(value_raw);
10420
10421   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10422   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10423 }
10424
10425 void InitMenuDesignSettings_Static(void)
10426 {
10427   int i;
10428
10429   // always start with reliable default values from static default config
10430   for (i = 0; image_config_vars[i].token != NULL; i++)
10431   {
10432     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10433
10434     if (value != NULL)
10435       *image_config_vars[i].value =
10436         get_token_parameter_value(image_config_vars[i].token, value);
10437   }
10438 }
10439
10440 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10441 {
10442   int i;
10443
10444   // the following initializes hierarchical values from static configuration
10445
10446   // special case: initialize "ARG_DEFAULT" values in static default config
10447   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10448   titlescreen_initial_first_default.fade_mode  =
10449     title_initial_first_default.fade_mode;
10450   titlescreen_initial_first_default.fade_delay =
10451     title_initial_first_default.fade_delay;
10452   titlescreen_initial_first_default.post_delay =
10453     title_initial_first_default.post_delay;
10454   titlescreen_initial_first_default.auto_delay =
10455     title_initial_first_default.auto_delay;
10456   titlescreen_initial_first_default.auto_delay_unit =
10457     title_initial_first_default.auto_delay_unit;
10458   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
10459   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10460   titlescreen_first_default.post_delay = title_first_default.post_delay;
10461   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10462   titlescreen_first_default.auto_delay_unit =
10463     title_first_default.auto_delay_unit;
10464   titlemessage_initial_first_default.fade_mode  =
10465     title_initial_first_default.fade_mode;
10466   titlemessage_initial_first_default.fade_delay =
10467     title_initial_first_default.fade_delay;
10468   titlemessage_initial_first_default.post_delay =
10469     title_initial_first_default.post_delay;
10470   titlemessage_initial_first_default.auto_delay =
10471     title_initial_first_default.auto_delay;
10472   titlemessage_initial_first_default.auto_delay_unit =
10473     title_initial_first_default.auto_delay_unit;
10474   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
10475   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10476   titlemessage_first_default.post_delay = title_first_default.post_delay;
10477   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10478   titlemessage_first_default.auto_delay_unit =
10479     title_first_default.auto_delay_unit;
10480
10481   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
10482   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10483   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10484   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10485   titlescreen_initial_default.auto_delay_unit =
10486     title_initial_default.auto_delay_unit;
10487   titlescreen_default.fade_mode  = title_default.fade_mode;
10488   titlescreen_default.fade_delay = title_default.fade_delay;
10489   titlescreen_default.post_delay = title_default.post_delay;
10490   titlescreen_default.auto_delay = title_default.auto_delay;
10491   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10492   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
10493   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10494   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10495   titlemessage_initial_default.auto_delay_unit =
10496     title_initial_default.auto_delay_unit;
10497   titlemessage_default.fade_mode  = title_default.fade_mode;
10498   titlemessage_default.fade_delay = title_default.fade_delay;
10499   titlemessage_default.post_delay = title_default.post_delay;
10500   titlemessage_default.auto_delay = title_default.auto_delay;
10501   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10502
10503   // special case: initialize "ARG_DEFAULT" values in static default config
10504   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10505   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10506   {
10507     titlescreen_initial_first[i] = titlescreen_initial_first_default;
10508     titlescreen_first[i] = titlescreen_first_default;
10509     titlemessage_initial_first[i] = titlemessage_initial_first_default;
10510     titlemessage_first[i] = titlemessage_first_default;
10511
10512     titlescreen_initial[i] = titlescreen_initial_default;
10513     titlescreen[i] = titlescreen_default;
10514     titlemessage_initial[i] = titlemessage_initial_default;
10515     titlemessage[i] = titlemessage_default;
10516   }
10517
10518   // special case: initialize "ARG_DEFAULT" values in static default config
10519   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10520   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10521   {
10522     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
10523       continue;
10524
10525     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10526     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10527     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10528   }
10529
10530   // special case: initialize "ARG_DEFAULT" values in static default config
10531   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10532   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10533   {
10534     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10535     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10536     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10537
10538     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
10539       continue;
10540
10541     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10542   }
10543 }
10544
10545 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10546 {
10547   static struct
10548   {
10549     struct XY *dst, *src;
10550   }
10551   game_buttons_xy[] =
10552   {
10553     { &game.button.save,        &game.button.stop       },
10554     { &game.button.pause2,      &game.button.pause      },
10555     { &game.button.load,        &game.button.play       },
10556     { &game.button.undo,        &game.button.stop       },
10557     { &game.button.redo,        &game.button.play       },
10558
10559     { NULL,                     NULL                    }
10560   };
10561   int i, j;
10562
10563   // special case: initialize later added SETUP list size from LEVELS value
10564   if (menu.list_size[GAME_MODE_SETUP] == -1)
10565     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10566
10567   // set default position for snapshot buttons to stop/pause/play buttons
10568   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10569     if ((*game_buttons_xy[i].dst).x == -1 &&
10570         (*game_buttons_xy[i].dst).y == -1)
10571       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10572
10573   // --------------------------------------------------------------------------
10574   // dynamic viewports (including playfield margins, borders and alignments)
10575   // --------------------------------------------------------------------------
10576
10577   // dynamic viewports currently only supported for landscape mode
10578   int display_width  = MAX(video.display_width, video.display_height);
10579   int display_height = MIN(video.display_width, video.display_height);
10580
10581   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10582   {
10583     struct RectWithBorder *vp_window    = &viewport.window[i];
10584     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10585     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
10586     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
10587     boolean dynamic_window_width     = (vp_window->min_width     != -1);
10588     boolean dynamic_window_height    = (vp_window->min_height    != -1);
10589     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
10590     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10591
10592     // adjust window size if min/max width/height is specified
10593
10594     if (vp_window->min_width != -1)
10595     {
10596       int window_width = display_width;
10597
10598       // when using static window height, use aspect ratio of display
10599       if (vp_window->min_height == -1)
10600         window_width = vp_window->height * display_width / display_height;
10601
10602       vp_window->width = MAX(vp_window->min_width, window_width);
10603     }
10604
10605     if (vp_window->min_height != -1)
10606     {
10607       int window_height = display_height;
10608
10609       // when using static window width, use aspect ratio of display
10610       if (vp_window->min_width == -1)
10611         window_height = vp_window->width * display_height / display_width;
10612
10613       vp_window->height = MAX(vp_window->min_height, window_height);
10614     }
10615
10616     if (vp_window->max_width != -1)
10617       vp_window->width = MIN(vp_window->width, vp_window->max_width);
10618
10619     if (vp_window->max_height != -1)
10620       vp_window->height = MIN(vp_window->height, vp_window->max_height);
10621
10622     int playfield_width  = vp_window->width;
10623     int playfield_height = vp_window->height;
10624
10625     // adjust playfield size and position according to specified margins
10626
10627     playfield_width  -= vp_playfield->margin_left;
10628     playfield_width  -= vp_playfield->margin_right;
10629
10630     playfield_height -= vp_playfield->margin_top;
10631     playfield_height -= vp_playfield->margin_bottom;
10632
10633     // adjust playfield size if min/max width/height is specified
10634
10635     if (vp_playfield->min_width != -1)
10636       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10637
10638     if (vp_playfield->min_height != -1)
10639       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10640
10641     if (vp_playfield->max_width != -1)
10642       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10643
10644     if (vp_playfield->max_height != -1)
10645       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10646
10647     // adjust playfield position according to specified alignment
10648
10649     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10650       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10651     else if (vp_playfield->align == ALIGN_CENTER)
10652       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10653     else if (vp_playfield->align == ALIGN_RIGHT)
10654       vp_playfield->x += playfield_width - vp_playfield->width;
10655
10656     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10657       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10658     else if (vp_playfield->valign == VALIGN_MIDDLE)
10659       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10660     else if (vp_playfield->valign == VALIGN_BOTTOM)
10661       vp_playfield->y += playfield_height - vp_playfield->height;
10662
10663     vp_playfield->x += vp_playfield->margin_left;
10664     vp_playfield->y += vp_playfield->margin_top;
10665
10666     // adjust individual playfield borders if only default border is specified
10667
10668     if (vp_playfield->border_left == -1)
10669       vp_playfield->border_left = vp_playfield->border_size;
10670     if (vp_playfield->border_right == -1)
10671       vp_playfield->border_right = vp_playfield->border_size;
10672     if (vp_playfield->border_top == -1)
10673       vp_playfield->border_top = vp_playfield->border_size;
10674     if (vp_playfield->border_bottom == -1)
10675       vp_playfield->border_bottom = vp_playfield->border_size;
10676
10677     // set dynamic playfield borders if borders are specified as undefined
10678     // (but only if window size was dynamic and playfield size was static)
10679
10680     if (dynamic_window_width && !dynamic_playfield_width)
10681     {
10682       if (vp_playfield->border_left == -1)
10683       {
10684         vp_playfield->border_left = (vp_playfield->x -
10685                                      vp_playfield->margin_left);
10686         vp_playfield->x     -= vp_playfield->border_left;
10687         vp_playfield->width += vp_playfield->border_left;
10688       }
10689
10690       if (vp_playfield->border_right == -1)
10691       {
10692         vp_playfield->border_right = (vp_window->width -
10693                                       vp_playfield->x -
10694                                       vp_playfield->width -
10695                                       vp_playfield->margin_right);
10696         vp_playfield->width += vp_playfield->border_right;
10697       }
10698     }
10699
10700     if (dynamic_window_height && !dynamic_playfield_height)
10701     {
10702       if (vp_playfield->border_top == -1)
10703       {
10704         vp_playfield->border_top = (vp_playfield->y -
10705                                     vp_playfield->margin_top);
10706         vp_playfield->y      -= vp_playfield->border_top;
10707         vp_playfield->height += vp_playfield->border_top;
10708       }
10709
10710       if (vp_playfield->border_bottom == -1)
10711       {
10712         vp_playfield->border_bottom = (vp_window->height -
10713                                        vp_playfield->y -
10714                                        vp_playfield->height -
10715                                        vp_playfield->margin_bottom);
10716         vp_playfield->height += vp_playfield->border_bottom;
10717       }
10718     }
10719
10720     // adjust playfield size to be a multiple of a defined alignment tile size
10721
10722     int align_size = vp_playfield->align_size;
10723     int playfield_xtiles = vp_playfield->width  / align_size;
10724     int playfield_ytiles = vp_playfield->height / align_size;
10725     int playfield_width_corrected  = playfield_xtiles * align_size;
10726     int playfield_height_corrected = playfield_ytiles * align_size;
10727     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10728                                  i == GFX_SPECIAL_ARG_EDITOR);
10729
10730     if (is_playfield_mode &&
10731         dynamic_playfield_width &&
10732         vp_playfield->width != playfield_width_corrected)
10733     {
10734       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10735
10736       vp_playfield->width = playfield_width_corrected;
10737
10738       if (vp_playfield->align == ALIGN_LEFT)
10739       {
10740         vp_playfield->border_left += playfield_xdiff;
10741       }
10742       else if (vp_playfield->align == ALIGN_RIGHT)
10743       {
10744         vp_playfield->border_right += playfield_xdiff;
10745       }
10746       else if (vp_playfield->align == ALIGN_CENTER)
10747       {
10748         int border_left_diff  = playfield_xdiff / 2;
10749         int border_right_diff = playfield_xdiff - border_left_diff;
10750
10751         vp_playfield->border_left  += border_left_diff;
10752         vp_playfield->border_right += border_right_diff;
10753       }
10754     }
10755
10756     if (is_playfield_mode &&
10757         dynamic_playfield_height &&
10758         vp_playfield->height != playfield_height_corrected)
10759     {
10760       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10761
10762       vp_playfield->height = playfield_height_corrected;
10763
10764       if (vp_playfield->valign == VALIGN_TOP)
10765       {
10766         vp_playfield->border_top += playfield_ydiff;
10767       }
10768       else if (vp_playfield->align == VALIGN_BOTTOM)
10769       {
10770         vp_playfield->border_right += playfield_ydiff;
10771       }
10772       else if (vp_playfield->align == VALIGN_MIDDLE)
10773       {
10774         int border_top_diff    = playfield_ydiff / 2;
10775         int border_bottom_diff = playfield_ydiff - border_top_diff;
10776
10777         vp_playfield->border_top    += border_top_diff;
10778         vp_playfield->border_bottom += border_bottom_diff;
10779       }
10780     }
10781
10782     // adjust door positions according to specified alignment
10783
10784     for (j = 0; j < 2; j++)
10785     {
10786       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10787
10788       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10789         vp_door->x = ALIGNED_VP_XPOS(vp_door);
10790       else if (vp_door->align == ALIGN_CENTER)
10791         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10792       else if (vp_door->align == ALIGN_RIGHT)
10793         vp_door->x += vp_window->width - vp_door->width;
10794
10795       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10796         vp_door->y = ALIGNED_VP_YPOS(vp_door);
10797       else if (vp_door->valign == VALIGN_MIDDLE)
10798         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10799       else if (vp_door->valign == VALIGN_BOTTOM)
10800         vp_door->y += vp_window->height - vp_door->height;
10801     }
10802   }
10803 }
10804
10805 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10806 {
10807   static struct
10808   {
10809     struct XYTileSize *dst, *src;
10810     int graphic;
10811   }
10812   editor_buttons_xy[] =
10813   {
10814     {
10815       &editor.button.element_left,      &editor.palette.element_left,
10816       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10817     },
10818     {
10819       &editor.button.element_middle,    &editor.palette.element_middle,
10820       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10821     },
10822     {
10823       &editor.button.element_right,     &editor.palette.element_right,
10824       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10825     },
10826
10827     { NULL,                     NULL                    }
10828   };
10829   int i;
10830
10831   // set default position for element buttons to element graphics
10832   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10833   {
10834     if ((*editor_buttons_xy[i].dst).x == -1 &&
10835         (*editor_buttons_xy[i].dst).y == -1)
10836     {
10837       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10838
10839       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10840
10841       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10842     }
10843   }
10844
10845   // adjust editor palette rows and columns if specified to be dynamic
10846
10847   if (editor.palette.cols == -1)
10848   {
10849     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10850     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10851     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10852
10853     editor.palette.cols = (vp_width - sc_width) / bt_width;
10854
10855     if (editor.palette.x == -1)
10856     {
10857       int palette_width = editor.palette.cols * bt_width + sc_width;
10858
10859       editor.palette.x = (vp_width - palette_width) / 2;
10860     }
10861   }
10862
10863   if (editor.palette.rows == -1)
10864   {
10865     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
10866     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
10867     int tx_height = getFontHeight(FONT_TEXT_2);
10868
10869     editor.palette.rows = (vp_height - tx_height) / bt_height;
10870
10871     if (editor.palette.y == -1)
10872     {
10873       int palette_height = editor.palette.rows * bt_height + tx_height;
10874
10875       editor.palette.y = (vp_height - palette_height) / 2;
10876     }
10877   }
10878 }
10879
10880 static void LoadMenuDesignSettingsFromFilename(char *filename)
10881 {
10882   static struct TitleFadingInfo tfi;
10883   static struct TitleMessageInfo tmi;
10884   static struct TokenInfo title_tokens[] =
10885   {
10886     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
10887     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
10888     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
10889     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
10890     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
10891
10892     { -1,               NULL,                   NULL                    }
10893   };
10894   static struct TokenInfo titlemessage_tokens[] =
10895   {
10896     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
10897     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
10898     { TYPE_INTEGER,     &tmi.width,             ".width"                },
10899     { TYPE_INTEGER,     &tmi.height,            ".height"               },
10900     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
10901     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
10902     { TYPE_INTEGER,     &tmi.align,             ".align"                },
10903     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
10904     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
10905     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
10906     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
10907     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
10908     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
10909     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
10910     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
10911     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
10912     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
10913     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
10914
10915     { -1,               NULL,                   NULL                    }
10916   };
10917   static struct
10918   {
10919     struct TitleFadingInfo *info;
10920     char *text;
10921   }
10922   title_info[] =
10923   {
10924     // initialize first titles from "enter screen" definitions, if defined
10925     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
10926     { &title_first_default,             "menu.enter_screen.TITLE"       },
10927
10928     // initialize title screens from "next screen" definitions, if defined
10929     { &title_initial_default,           "menu.next_screen.TITLE"        },
10930     { &title_default,                   "menu.next_screen.TITLE"        },
10931
10932     { NULL,                             NULL                            }
10933   };
10934   static struct
10935   {
10936     struct TitleMessageInfo *array;
10937     char *text;
10938   }
10939   titlemessage_arrays[] =
10940   {
10941     // initialize first titles from "enter screen" definitions, if defined
10942     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
10943     { titlescreen_first,                "menu.enter_screen.TITLE"       },
10944     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
10945     { titlemessage_first,               "menu.enter_screen.TITLE"       },
10946
10947     // initialize titles from "next screen" definitions, if defined
10948     { titlescreen_initial,              "menu.next_screen.TITLE"        },
10949     { titlescreen,                      "menu.next_screen.TITLE"        },
10950     { titlemessage_initial,             "menu.next_screen.TITLE"        },
10951     { titlemessage,                     "menu.next_screen.TITLE"        },
10952
10953     // overwrite titles with title definitions, if defined
10954     { titlescreen_initial_first,        "[title_initial]"               },
10955     { titlescreen_first,                "[title]"                       },
10956     { titlemessage_initial_first,       "[title_initial]"               },
10957     { titlemessage_first,               "[title]"                       },
10958
10959     { titlescreen_initial,              "[title_initial]"               },
10960     { titlescreen,                      "[title]"                       },
10961     { titlemessage_initial,             "[title_initial]"               },
10962     { titlemessage,                     "[title]"                       },
10963
10964     // overwrite titles with title screen/message definitions, if defined
10965     { titlescreen_initial_first,        "[titlescreen_initial]"         },
10966     { titlescreen_first,                "[titlescreen]"                 },
10967     { titlemessage_initial_first,       "[titlemessage_initial]"        },
10968     { titlemessage_first,               "[titlemessage]"                },
10969
10970     { titlescreen_initial,              "[titlescreen_initial]"         },
10971     { titlescreen,                      "[titlescreen]"                 },
10972     { titlemessage_initial,             "[titlemessage_initial]"        },
10973     { titlemessage,                     "[titlemessage]"                },
10974
10975     { NULL,                             NULL                            }
10976   };
10977   SetupFileHash *setup_file_hash;
10978   int i, j, k;
10979
10980   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10981     return;
10982
10983   // the following initializes hierarchical values from dynamic configuration
10984
10985   // special case: initialize with default values that may be overwritten
10986   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
10987   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10988   {
10989     struct TokenIntPtrInfo menu_config[] =
10990     {
10991       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
10992       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
10993       { "menu.list_size",       &menu.list_size[i]      }
10994     };
10995
10996     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10997     {
10998       char *token = menu_config[j].token;
10999       char *value = getHashEntry(setup_file_hash, token);
11000
11001       if (value != NULL)
11002         *menu_config[j].value = get_integer_from_string(value);
11003     }
11004   }
11005
11006   // special case: initialize with default values that may be overwritten
11007   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11008   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11009   {
11010     struct TokenIntPtrInfo menu_config[] =
11011     {
11012       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
11013       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
11014       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
11015     };
11016
11017     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11018     {
11019       char *token = menu_config[j].token;
11020       char *value = getHashEntry(setup_file_hash, token);
11021
11022       if (value != NULL)
11023         *menu_config[j].value = get_integer_from_string(value);
11024     }
11025   }
11026
11027   // special case: initialize with default values that may be overwritten
11028   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11029   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11030   {
11031     struct TokenIntPtrInfo menu_config[] =
11032     {
11033       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
11034       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
11035     };
11036
11037     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11038     {
11039       char *token = menu_config[j].token;
11040       char *value = getHashEntry(setup_file_hash, token);
11041
11042       if (value != NULL)
11043         *menu_config[j].value = get_integer_from_string(value);
11044     }
11045   }
11046
11047   // special case: initialize with default values that may be overwritten
11048   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11049   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11050   {
11051     struct TokenIntPtrInfo menu_config[] =
11052     {
11053       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
11054       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
11055       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
11056       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
11057       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
11058       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
11059       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
11060       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
11061       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
11062     };
11063
11064     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11065     {
11066       char *token = menu_config[j].token;
11067       char *value = getHashEntry(setup_file_hash, token);
11068
11069       if (value != NULL)
11070         *menu_config[j].value = get_integer_from_string(value);
11071     }
11072   }
11073
11074   // special case: initialize with default values that may be overwritten
11075   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11076   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11077   {
11078     struct TokenIntPtrInfo menu_config[] =
11079     {
11080       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
11081       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11082       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11083       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
11084       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11085       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11086       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
11087       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
11088       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
11089     };
11090
11091     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11092     {
11093       char *token = menu_config[j].token;
11094       char *value = getHashEntry(setup_file_hash, token);
11095
11096       if (value != NULL)
11097         *menu_config[j].value = get_token_parameter_value(token, value);
11098     }
11099   }
11100
11101   // special case: initialize with default values that may be overwritten
11102   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11103   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11104   {
11105     struct
11106     {
11107       char *token_prefix;
11108       struct RectWithBorder *struct_ptr;
11109     }
11110     vp_struct[] =
11111     {
11112       { "viewport.window",      &viewport.window[i]     },
11113       { "viewport.playfield",   &viewport.playfield[i]  },
11114       { "viewport.door_1",      &viewport.door_1[i]     },
11115       { "viewport.door_2",      &viewport.door_2[i]     }
11116     };
11117
11118     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11119     {
11120       struct TokenIntPtrInfo vp_config[] =
11121       {
11122         { ".x",                 &vp_struct[j].struct_ptr->x             },
11123         { ".y",                 &vp_struct[j].struct_ptr->y             },
11124         { ".width",             &vp_struct[j].struct_ptr->width         },
11125         { ".height",            &vp_struct[j].struct_ptr->height        },
11126         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
11127         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
11128         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
11129         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
11130         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
11131         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
11132         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
11133         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
11134         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
11135         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
11136         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
11137         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
11138         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
11139         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
11140         { ".align",             &vp_struct[j].struct_ptr->align         },
11141         { ".valign",            &vp_struct[j].struct_ptr->valign        }
11142       };
11143
11144       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11145       {
11146         char *token = getStringCat2(vp_struct[j].token_prefix,
11147                                     vp_config[k].token);
11148         char *value = getHashEntry(setup_file_hash, token);
11149
11150         if (value != NULL)
11151           *vp_config[k].value = get_token_parameter_value(token, value);
11152
11153         free(token);
11154       }
11155     }
11156   }
11157
11158   // special case: initialize with default values that may be overwritten
11159   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11160   for (i = 0; title_info[i].info != NULL; i++)
11161   {
11162     struct TitleFadingInfo *info = title_info[i].info;
11163     char *base_token = title_info[i].text;
11164
11165     for (j = 0; title_tokens[j].type != -1; j++)
11166     {
11167       char *token = getStringCat2(base_token, title_tokens[j].text);
11168       char *value = getHashEntry(setup_file_hash, token);
11169
11170       if (value != NULL)
11171       {
11172         int parameter_value = get_token_parameter_value(token, value);
11173
11174         tfi = *info;
11175
11176         *(int *)title_tokens[j].value = (int)parameter_value;
11177
11178         *info = tfi;
11179       }
11180
11181       free(token);
11182     }
11183   }
11184
11185   // special case: initialize with default values that may be overwritten
11186   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11187   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11188   {
11189     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11190     char *base_token = titlemessage_arrays[i].text;
11191
11192     for (j = 0; titlemessage_tokens[j].type != -1; j++)
11193     {
11194       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11195       char *value = getHashEntry(setup_file_hash, token);
11196
11197       if (value != NULL)
11198       {
11199         int parameter_value = get_token_parameter_value(token, value);
11200
11201         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11202         {
11203           tmi = array[k];
11204
11205           if (titlemessage_tokens[j].type == TYPE_INTEGER)
11206             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
11207           else
11208             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11209
11210           array[k] = tmi;
11211         }
11212       }
11213
11214       free(token);
11215     }
11216   }
11217
11218   // special case: check if network and preview player positions are redefined,
11219   // to compare this later against the main menu level preview being redefined
11220   struct TokenIntPtrInfo menu_config_players[] =
11221   {
11222     { "main.network_players.x", &menu.main.network_players.redefined    },
11223     { "main.network_players.y", &menu.main.network_players.redefined    },
11224     { "main.preview_players.x", &menu.main.preview_players.redefined    },
11225     { "main.preview_players.y", &menu.main.preview_players.redefined    },
11226     { "preview.x",              &preview.redefined                      },
11227     { "preview.y",              &preview.redefined                      }
11228   };
11229
11230   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11231     *menu_config_players[i].value = FALSE;
11232
11233   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11234     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11235       *menu_config_players[i].value = TRUE;
11236
11237   // read (and overwrite with) values that may be specified in config file
11238   for (i = 0; image_config_vars[i].token != NULL; i++)
11239   {
11240     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11241
11242     // (ignore definitions set to "[DEFAULT]" which are already initialized)
11243     if (value != NULL && !strEqual(value, ARG_DEFAULT))
11244       *image_config_vars[i].value =
11245         get_token_parameter_value(image_config_vars[i].token, value);
11246   }
11247
11248   freeSetupFileHash(setup_file_hash);
11249 }
11250
11251 void LoadMenuDesignSettings(void)
11252 {
11253   char *filename_base = UNDEFINED_FILENAME, *filename_local;
11254
11255   InitMenuDesignSettings_Static();
11256   InitMenuDesignSettings_SpecialPreProcessing();
11257
11258   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11259   {
11260     // first look for special settings configured in level series config
11261     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11262
11263     if (fileExists(filename_base))
11264       LoadMenuDesignSettingsFromFilename(filename_base);
11265   }
11266
11267   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11268
11269   if (filename_local != NULL && !strEqual(filename_base, filename_local))
11270     LoadMenuDesignSettingsFromFilename(filename_local);
11271
11272   InitMenuDesignSettings_SpecialPostProcessing();
11273 }
11274
11275 void LoadMenuDesignSettings_AfterGraphics(void)
11276 {
11277   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11278 }
11279
11280 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11281 {
11282   char *filename = getEditorSetupFilename();
11283   SetupFileList *setup_file_list, *list;
11284   SetupFileHash *element_hash;
11285   int num_unknown_tokens = 0;
11286   int i;
11287
11288   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11289     return;
11290
11291   element_hash = newSetupFileHash();
11292
11293   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11294     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11295
11296   // determined size may be larger than needed (due to unknown elements)
11297   *num_elements = 0;
11298   for (list = setup_file_list; list != NULL; list = list->next)
11299     (*num_elements)++;
11300
11301   // add space for up to 3 more elements for padding that may be needed
11302   *num_elements += 3;
11303
11304   // free memory for old list of elements, if needed
11305   checked_free(*elements);
11306
11307   // allocate memory for new list of elements
11308   *elements = checked_malloc(*num_elements * sizeof(int));
11309
11310   *num_elements = 0;
11311   for (list = setup_file_list; list != NULL; list = list->next)
11312   {
11313     char *value = getHashEntry(element_hash, list->token);
11314
11315     if (value == NULL)          // try to find obsolete token mapping
11316     {
11317       char *mapped_token = get_mapped_token(list->token);
11318
11319       if (mapped_token != NULL)
11320       {
11321         value = getHashEntry(element_hash, mapped_token);
11322
11323         free(mapped_token);
11324       }
11325     }
11326
11327     if (value != NULL)
11328     {
11329       (*elements)[(*num_elements)++] = atoi(value);
11330     }
11331     else
11332     {
11333       if (num_unknown_tokens == 0)
11334       {
11335         Error(ERR_INFO_LINE, "-");
11336         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11337         Error(ERR_INFO, "- config file: '%s'", filename);
11338
11339         num_unknown_tokens++;
11340       }
11341
11342       Error(ERR_INFO, "- token: '%s'", list->token);
11343     }
11344   }
11345
11346   if (num_unknown_tokens > 0)
11347     Error(ERR_INFO_LINE, "-");
11348
11349   while (*num_elements % 4)     // pad with empty elements, if needed
11350     (*elements)[(*num_elements)++] = EL_EMPTY;
11351
11352   freeSetupFileList(setup_file_list);
11353   freeSetupFileHash(element_hash);
11354
11355 #if 0
11356   for (i = 0; i < *num_elements; i++)
11357     printf("editor: element '%s' [%d]\n",
11358            element_info[(*elements)[i]].token_name, (*elements)[i]);
11359 #endif
11360 }
11361
11362 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11363                                                      boolean is_sound)
11364 {
11365   SetupFileHash *setup_file_hash = NULL;
11366   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11367   char *filename_music, *filename_prefix, *filename_info;
11368   struct
11369   {
11370     char *token;
11371     char **value_ptr;
11372   }
11373   token_to_value_ptr[] =
11374   {
11375     { "title_header",   &tmp_music_file_info.title_header       },
11376     { "artist_header",  &tmp_music_file_info.artist_header      },
11377     { "album_header",   &tmp_music_file_info.album_header       },
11378     { "year_header",    &tmp_music_file_info.year_header        },
11379
11380     { "title",          &tmp_music_file_info.title              },
11381     { "artist",         &tmp_music_file_info.artist             },
11382     { "album",          &tmp_music_file_info.album              },
11383     { "year",           &tmp_music_file_info.year               },
11384
11385     { NULL,             NULL                                    },
11386   };
11387   int i;
11388
11389   filename_music = (is_sound ? getCustomSoundFilename(basename) :
11390                     getCustomMusicFilename(basename));
11391
11392   if (filename_music == NULL)
11393     return NULL;
11394
11395   // ---------- try to replace file extension ----------
11396
11397   filename_prefix = getStringCopy(filename_music);
11398   if (strrchr(filename_prefix, '.') != NULL)
11399     *strrchr(filename_prefix, '.') = '\0';
11400   filename_info = getStringCat2(filename_prefix, ".txt");
11401
11402   if (fileExists(filename_info))
11403     setup_file_hash = loadSetupFileHash(filename_info);
11404
11405   free(filename_prefix);
11406   free(filename_info);
11407
11408   if (setup_file_hash == NULL)
11409   {
11410     // ---------- try to add file extension ----------
11411
11412     filename_prefix = getStringCopy(filename_music);
11413     filename_info = getStringCat2(filename_prefix, ".txt");
11414
11415     if (fileExists(filename_info))
11416       setup_file_hash = loadSetupFileHash(filename_info);
11417
11418     free(filename_prefix);
11419     free(filename_info);
11420   }
11421
11422   if (setup_file_hash == NULL)
11423     return NULL;
11424
11425   // ---------- music file info found ----------
11426
11427   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11428
11429   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11430   {
11431     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11432
11433     *token_to_value_ptr[i].value_ptr =
11434       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11435   }
11436
11437   tmp_music_file_info.basename = getStringCopy(basename);
11438   tmp_music_file_info.music = music;
11439   tmp_music_file_info.is_sound = is_sound;
11440
11441   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11442   *new_music_file_info = tmp_music_file_info;
11443
11444   return new_music_file_info;
11445 }
11446
11447 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11448 {
11449   return get_music_file_info_ext(basename, music, FALSE);
11450 }
11451
11452 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11453 {
11454   return get_music_file_info_ext(basename, sound, TRUE);
11455 }
11456
11457 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11458                                      char *basename, boolean is_sound)
11459 {
11460   for (; list != NULL; list = list->next)
11461     if (list->is_sound == is_sound && strEqual(list->basename, basename))
11462       return TRUE;
11463
11464   return FALSE;
11465 }
11466
11467 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11468 {
11469   return music_info_listed_ext(list, basename, FALSE);
11470 }
11471
11472 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11473 {
11474   return music_info_listed_ext(list, basename, TRUE);
11475 }
11476
11477 void LoadMusicInfo(void)
11478 {
11479   char *music_directory = getCustomMusicDirectory();
11480   int num_music = getMusicListSize();
11481   int num_music_noconf = 0;
11482   int num_sounds = getSoundListSize();
11483   Directory *dir;
11484   DirectoryEntry *dir_entry;
11485   struct FileInfo *music, *sound;
11486   struct MusicFileInfo *next, **new;
11487   int i;
11488
11489   while (music_file_info != NULL)
11490   {
11491     next = music_file_info->next;
11492
11493     checked_free(music_file_info->basename);
11494
11495     checked_free(music_file_info->title_header);
11496     checked_free(music_file_info->artist_header);
11497     checked_free(music_file_info->album_header);
11498     checked_free(music_file_info->year_header);
11499
11500     checked_free(music_file_info->title);
11501     checked_free(music_file_info->artist);
11502     checked_free(music_file_info->album);
11503     checked_free(music_file_info->year);
11504
11505     free(music_file_info);
11506
11507     music_file_info = next;
11508   }
11509
11510   new = &music_file_info;
11511
11512   for (i = 0; i < num_music; i++)
11513   {
11514     music = getMusicListEntry(i);
11515
11516     if (music->filename == NULL)
11517       continue;
11518
11519     if (strEqual(music->filename, UNDEFINED_FILENAME))
11520       continue;
11521
11522     // a configured file may be not recognized as music
11523     if (!FileIsMusic(music->filename))
11524       continue;
11525
11526     if (!music_info_listed(music_file_info, music->filename))
11527     {
11528       *new = get_music_file_info(music->filename, i);
11529
11530       if (*new != NULL)
11531         new = &(*new)->next;
11532     }
11533   }
11534
11535   if ((dir = openDirectory(music_directory)) == NULL)
11536   {
11537     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
11538     return;
11539   }
11540
11541   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
11542   {
11543     char *basename = dir_entry->basename;
11544     boolean music_already_used = FALSE;
11545     int i;
11546
11547     // skip all music files that are configured in music config file
11548     for (i = 0; i < num_music; i++)
11549     {
11550       music = getMusicListEntry(i);
11551
11552       if (music->filename == NULL)
11553         continue;
11554
11555       if (strEqual(basename, music->filename))
11556       {
11557         music_already_used = TRUE;
11558         break;
11559       }
11560     }
11561
11562     if (music_already_used)
11563       continue;
11564
11565     if (!FileIsMusic(dir_entry->filename))
11566       continue;
11567
11568     if (!music_info_listed(music_file_info, basename))
11569     {
11570       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11571
11572       if (*new != NULL)
11573         new = &(*new)->next;
11574     }
11575
11576     num_music_noconf++;
11577   }
11578
11579   closeDirectory(dir);
11580
11581   for (i = 0; i < num_sounds; i++)
11582   {
11583     sound = getSoundListEntry(i);
11584
11585     if (sound->filename == NULL)
11586       continue;
11587
11588     if (strEqual(sound->filename, UNDEFINED_FILENAME))
11589       continue;
11590
11591     // a configured file may be not recognized as sound
11592     if (!FileIsSound(sound->filename))
11593       continue;
11594
11595     if (!sound_info_listed(music_file_info, sound->filename))
11596     {
11597       *new = get_sound_file_info(sound->filename, i);
11598       if (*new != NULL)
11599         new = &(*new)->next;
11600     }
11601   }
11602 }
11603
11604 static void add_helpanim_entry(int element, int action, int direction,
11605                                int delay, int *num_list_entries)
11606 {
11607   struct HelpAnimInfo *new_list_entry;
11608   (*num_list_entries)++;
11609
11610   helpanim_info =
11611     checked_realloc(helpanim_info,
11612                     *num_list_entries * sizeof(struct HelpAnimInfo));
11613   new_list_entry = &helpanim_info[*num_list_entries - 1];
11614
11615   new_list_entry->element = element;
11616   new_list_entry->action = action;
11617   new_list_entry->direction = direction;
11618   new_list_entry->delay = delay;
11619 }
11620
11621 static void print_unknown_token(char *filename, char *token, int token_nr)
11622 {
11623   if (token_nr == 0)
11624   {
11625     Error(ERR_INFO_LINE, "-");
11626     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11627     Error(ERR_INFO, "- config file: '%s'", filename);
11628   }
11629
11630   Error(ERR_INFO, "- token: '%s'", token);
11631 }
11632
11633 static void print_unknown_token_end(int token_nr)
11634 {
11635   if (token_nr > 0)
11636     Error(ERR_INFO_LINE, "-");
11637 }
11638
11639 void LoadHelpAnimInfo(void)
11640 {
11641   char *filename = getHelpAnimFilename();
11642   SetupFileList *setup_file_list = NULL, *list;
11643   SetupFileHash *element_hash, *action_hash, *direction_hash;
11644   int num_list_entries = 0;
11645   int num_unknown_tokens = 0;
11646   int i;
11647
11648   if (fileExists(filename))
11649     setup_file_list = loadSetupFileList(filename);
11650
11651   if (setup_file_list == NULL)
11652   {
11653     // use reliable default values from static configuration
11654     SetupFileList *insert_ptr;
11655
11656     insert_ptr = setup_file_list =
11657       newSetupFileList(helpanim_config[0].token,
11658                        helpanim_config[0].value);
11659
11660     for (i = 1; helpanim_config[i].token; i++)
11661       insert_ptr = addListEntry(insert_ptr,
11662                                 helpanim_config[i].token,
11663                                 helpanim_config[i].value);
11664   }
11665
11666   element_hash   = newSetupFileHash();
11667   action_hash    = newSetupFileHash();
11668   direction_hash = newSetupFileHash();
11669
11670   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11671     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11672
11673   for (i = 0; i < NUM_ACTIONS; i++)
11674     setHashEntry(action_hash, element_action_info[i].suffix,
11675                  i_to_a(element_action_info[i].value));
11676
11677   // do not store direction index (bit) here, but direction value!
11678   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11679     setHashEntry(direction_hash, element_direction_info[i].suffix,
11680                  i_to_a(1 << element_direction_info[i].value));
11681
11682   for (list = setup_file_list; list != NULL; list = list->next)
11683   {
11684     char *element_token, *action_token, *direction_token;
11685     char *element_value, *action_value, *direction_value;
11686     int delay = atoi(list->value);
11687
11688     if (strEqual(list->token, "end"))
11689     {
11690       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11691
11692       continue;
11693     }
11694
11695     /* first try to break element into element/action/direction parts;
11696        if this does not work, also accept combined "element[.act][.dir]"
11697        elements (like "dynamite.active"), which are unique elements */
11698
11699     if (strchr(list->token, '.') == NULL)       // token contains no '.'
11700     {
11701       element_value = getHashEntry(element_hash, list->token);
11702       if (element_value != NULL)        // element found
11703         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11704                            &num_list_entries);
11705       else
11706       {
11707         // no further suffixes found -- this is not an element
11708         print_unknown_token(filename, list->token, num_unknown_tokens++);
11709       }
11710
11711       continue;
11712     }
11713
11714     // token has format "<prefix>.<something>"
11715
11716     action_token = strchr(list->token, '.');    // suffix may be action ...
11717     direction_token = action_token;             // ... or direction
11718
11719     element_token = getStringCopy(list->token);
11720     *strchr(element_token, '.') = '\0';
11721
11722     element_value = getHashEntry(element_hash, element_token);
11723
11724     if (element_value == NULL)          // this is no element
11725     {
11726       element_value = getHashEntry(element_hash, list->token);
11727       if (element_value != NULL)        // combined element found
11728         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11729                            &num_list_entries);
11730       else
11731         print_unknown_token(filename, list->token, num_unknown_tokens++);
11732
11733       free(element_token);
11734
11735       continue;
11736     }
11737
11738     action_value = getHashEntry(action_hash, action_token);
11739
11740     if (action_value != NULL)           // action found
11741     {
11742       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11743                     &num_list_entries);
11744
11745       free(element_token);
11746
11747       continue;
11748     }
11749
11750     direction_value = getHashEntry(direction_hash, direction_token);
11751
11752     if (direction_value != NULL)        // direction found
11753     {
11754       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11755                          &num_list_entries);
11756
11757       free(element_token);
11758
11759       continue;
11760     }
11761
11762     if (strchr(action_token + 1, '.') == NULL)
11763     {
11764       // no further suffixes found -- this is not an action nor direction
11765
11766       element_value = getHashEntry(element_hash, list->token);
11767       if (element_value != NULL)        // combined element found
11768         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11769                            &num_list_entries);
11770       else
11771         print_unknown_token(filename, list->token, num_unknown_tokens++);
11772
11773       free(element_token);
11774
11775       continue;
11776     }
11777
11778     // token has format "<prefix>.<suffix>.<something>"
11779
11780     direction_token = strchr(action_token + 1, '.');
11781
11782     action_token = getStringCopy(action_token);
11783     *strchr(action_token + 1, '.') = '\0';
11784
11785     action_value = getHashEntry(action_hash, action_token);
11786
11787     if (action_value == NULL)           // this is no action
11788     {
11789       element_value = getHashEntry(element_hash, list->token);
11790       if (element_value != NULL)        // combined element found
11791         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11792                            &num_list_entries);
11793       else
11794         print_unknown_token(filename, list->token, num_unknown_tokens++);
11795
11796       free(element_token);
11797       free(action_token);
11798
11799       continue;
11800     }
11801
11802     direction_value = getHashEntry(direction_hash, direction_token);
11803
11804     if (direction_value != NULL)        // direction found
11805     {
11806       add_helpanim_entry(atoi(element_value), atoi(action_value),
11807                          atoi(direction_value), delay, &num_list_entries);
11808
11809       free(element_token);
11810       free(action_token);
11811
11812       continue;
11813     }
11814
11815     // this is no direction
11816
11817     element_value = getHashEntry(element_hash, list->token);
11818     if (element_value != NULL)          // combined element found
11819       add_helpanim_entry(atoi(element_value), -1, -1, delay,
11820                          &num_list_entries);
11821     else
11822       print_unknown_token(filename, list->token, num_unknown_tokens++);
11823
11824     free(element_token);
11825     free(action_token);
11826   }
11827
11828   print_unknown_token_end(num_unknown_tokens);
11829
11830   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11831   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
11832
11833   freeSetupFileList(setup_file_list);
11834   freeSetupFileHash(element_hash);
11835   freeSetupFileHash(action_hash);
11836   freeSetupFileHash(direction_hash);
11837
11838 #if 0
11839   for (i = 0; i < num_list_entries; i++)
11840     printf("::: '%s': %d, %d, %d => %d\n",
11841            EL_NAME(helpanim_info[i].element),
11842            helpanim_info[i].element,
11843            helpanim_info[i].action,
11844            helpanim_info[i].direction,
11845            helpanim_info[i].delay);
11846 #endif
11847 }
11848
11849 void LoadHelpTextInfo(void)
11850 {
11851   char *filename = getHelpTextFilename();
11852   int i;
11853
11854   if (helptext_info != NULL)
11855   {
11856     freeSetupFileHash(helptext_info);
11857     helptext_info = NULL;
11858   }
11859
11860   if (fileExists(filename))
11861     helptext_info = loadSetupFileHash(filename);
11862
11863   if (helptext_info == NULL)
11864   {
11865     // use reliable default values from static configuration
11866     helptext_info = newSetupFileHash();
11867
11868     for (i = 0; helptext_config[i].token; i++)
11869       setHashEntry(helptext_info,
11870                    helptext_config[i].token,
11871                    helptext_config[i].value);
11872   }
11873
11874 #if 0
11875   BEGIN_HASH_ITERATION(helptext_info, itr)
11876   {
11877     printf("::: '%s' => '%s'\n",
11878            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
11879   }
11880   END_HASH_ITERATION(hash, itr)
11881 #endif
11882 }
11883
11884
11885 // ----------------------------------------------------------------------------
11886 // convert levels
11887 // ----------------------------------------------------------------------------
11888
11889 #define MAX_NUM_CONVERT_LEVELS          1000
11890
11891 void ConvertLevels(void)
11892 {
11893   static LevelDirTree *convert_leveldir = NULL;
11894   static int convert_level_nr = -1;
11895   static int num_levels_handled = 0;
11896   static int num_levels_converted = 0;
11897   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
11898   int i;
11899
11900   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
11901                                                global.convert_leveldir);
11902
11903   if (convert_leveldir == NULL)
11904     Error(ERR_EXIT, "no such level identifier: '%s'",
11905           global.convert_leveldir);
11906
11907   leveldir_current = convert_leveldir;
11908
11909   if (global.convert_level_nr != -1)
11910   {
11911     convert_leveldir->first_level = global.convert_level_nr;
11912     convert_leveldir->last_level  = global.convert_level_nr;
11913   }
11914
11915   convert_level_nr = convert_leveldir->first_level;
11916
11917   PrintLine("=", 79);
11918   Print("Converting levels\n");
11919   PrintLine("-", 79);
11920   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
11921   Print("Level series name:       '%s'\n", convert_leveldir->name);
11922   Print("Level series author:     '%s'\n", convert_leveldir->author);
11923   Print("Number of levels:        %d\n",   convert_leveldir->levels);
11924   PrintLine("=", 79);
11925   Print("\n");
11926
11927   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11928     levels_failed[i] = FALSE;
11929
11930   while (convert_level_nr <= convert_leveldir->last_level)
11931   {
11932     char *level_filename;
11933     boolean new_level;
11934
11935     level_nr = convert_level_nr++;
11936
11937     Print("Level %03d: ", level_nr);
11938
11939     LoadLevel(level_nr);
11940     if (level.no_level_file || level.no_valid_file)
11941     {
11942       Print("(no level)\n");
11943       continue;
11944     }
11945
11946     Print("converting level ... ");
11947
11948     level_filename = getDefaultLevelFilename(level_nr);
11949     new_level = !fileExists(level_filename);
11950
11951     if (new_level)
11952     {
11953       SaveLevel(level_nr);
11954
11955       num_levels_converted++;
11956
11957       Print("converted.\n");
11958     }
11959     else
11960     {
11961       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
11962         levels_failed[level_nr] = TRUE;
11963
11964       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
11965     }
11966
11967     num_levels_handled++;
11968   }
11969
11970   Print("\n");
11971   PrintLine("=", 79);
11972   Print("Number of levels handled: %d\n", num_levels_handled);
11973   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
11974          (num_levels_handled ?
11975           num_levels_converted * 100 / num_levels_handled : 0));
11976   PrintLine("-", 79);
11977   Print("Summary (for automatic parsing by scripts):\n");
11978   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
11979          convert_leveldir->identifier, num_levels_converted,
11980          num_levels_handled,
11981          (num_levels_handled ?
11982           num_levels_converted * 100 / num_levels_handled : 0));
11983
11984   if (num_levels_handled != num_levels_converted)
11985   {
11986     Print(", FAILED:");
11987     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11988       if (levels_failed[i])
11989         Print(" %03d", i);
11990   }
11991
11992   Print("\n");
11993   PrintLine("=", 79);
11994
11995   CloseAllAndExit(0);
11996 }
11997
11998
11999 // ----------------------------------------------------------------------------
12000 // create and save images for use in level sketches (raw BMP format)
12001 // ----------------------------------------------------------------------------
12002
12003 void CreateLevelSketchImages(void)
12004 {
12005   Bitmap *bitmap1;
12006   Bitmap *bitmap2;
12007   int i;
12008
12009   InitElementPropertiesGfxElement();
12010
12011   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12012   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12013
12014   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12015   {
12016     int element = getMappedElement(i);
12017     char basename1[16];
12018     char basename2[16];
12019     char *filename1;
12020     char *filename2;
12021
12022     sprintf(basename1, "%04d.bmp", i);
12023     sprintf(basename2, "%04ds.bmp", i);
12024
12025     filename1 = getPath2(global.create_images_dir, basename1);
12026     filename2 = getPath2(global.create_images_dir, basename2);
12027
12028     DrawSizedElement(0, 0, element, TILESIZE);
12029     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12030
12031     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12032       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
12033
12034     DrawSizedElement(0, 0, element, MINI_TILESIZE);
12035     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12036
12037     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12038       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
12039
12040     free(filename1);
12041     free(filename2);
12042
12043     // create corresponding SQL statements (for normal and small images)
12044     if (i < 1000)
12045     {
12046       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12047       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12048     }
12049
12050     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12051     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12052
12053     // optional: create content for forum level sketch demonstration post
12054     if (options.debug)
12055       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12056   }
12057
12058   FreeBitmap(bitmap1);
12059   FreeBitmap(bitmap2);
12060
12061   if (options.debug)
12062     fprintf(stderr, "\n");
12063
12064   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
12065
12066   CloseAllAndExit(0);
12067 }
12068
12069
12070 // ----------------------------------------------------------------------------
12071 // create and save images for custom and group elements (raw BMP format)
12072 // ----------------------------------------------------------------------------
12073
12074 void CreateCustomElementImages(char *directory)
12075 {
12076   char *src_basename = "RocksCE-template.ilbm";
12077   char *dst_basename = "RocksCE.bmp";
12078   char *src_filename = getPath2(directory, src_basename);
12079   char *dst_filename = getPath2(directory, dst_basename);
12080   Bitmap *src_bitmap;
12081   Bitmap *bitmap;
12082   int yoffset_ce = 0;
12083   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12084   int i;
12085
12086   InitVideoDefaults();
12087
12088   ReCreateBitmap(&backbuffer, video.width, video.height);
12089
12090   src_bitmap = LoadImage(src_filename);
12091
12092   bitmap = CreateBitmap(TILEX * 16 * 2,
12093                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12094                         DEFAULT_DEPTH);
12095
12096   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12097   {
12098     int x = i % 16;
12099     int y = i / 16;
12100     int ii = i + 1;
12101     int j;
12102
12103     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12104                TILEX * x, TILEY * y + yoffset_ce);
12105
12106     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12107                TILEX, TILEY,
12108                TILEX * x + TILEX * 16,
12109                TILEY * y + yoffset_ce);
12110
12111     for (j = 2; j >= 0; j--)
12112     {
12113       int c = ii % 10;
12114
12115       BlitBitmap(src_bitmap, bitmap,
12116                  TILEX + c * 7, 0, 6, 10,
12117                  TILEX * x + 6 + j * 7,
12118                  TILEY * y + 11 + yoffset_ce);
12119
12120       BlitBitmap(src_bitmap, bitmap,
12121                  TILEX + c * 8, TILEY, 6, 10,
12122                  TILEX * 16 + TILEX * x + 6 + j * 8,
12123                  TILEY * y + 10 + yoffset_ce);
12124
12125       ii /= 10;
12126     }
12127   }
12128
12129   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12130   {
12131     int x = i % 16;
12132     int y = i / 16;
12133     int ii = i + 1;
12134     int j;
12135
12136     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12137                TILEX * x, TILEY * y + yoffset_ge);
12138
12139     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12140                TILEX, TILEY,
12141                TILEX * x + TILEX * 16,
12142                TILEY * y + yoffset_ge);
12143
12144     for (j = 1; j >= 0; j--)
12145     {
12146       int c = ii % 10;
12147
12148       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12149                  TILEX * x + 6 + j * 10,
12150                  TILEY * y + 11 + yoffset_ge);
12151
12152       BlitBitmap(src_bitmap, bitmap,
12153                  TILEX + c * 8, TILEY + 12, 6, 10,
12154                  TILEX * 16 + TILEX * x + 10 + j * 8,
12155                  TILEY * y + 10 + yoffset_ge);
12156
12157       ii /= 10;
12158     }
12159   }
12160
12161   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12162     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
12163
12164   FreeBitmap(bitmap);
12165
12166   CloseAllAndExit(0);
12167 }