renamed playfield variable
[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       Tile[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 SaveTapeToFilename(char *filename)
8148 {
8149   FILE *file;
8150   int tape_pos_size;
8151   int info_chunk_size;
8152   int body_chunk_size;
8153
8154   if (!(file = fopen(filename, MODE_WRITE)))
8155   {
8156     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8157     return;
8158   }
8159
8160   tape_pos_size = getTapePosSize(&tape);
8161
8162   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8163   body_chunk_size = tape_pos_size * tape.length;
8164
8165   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8166   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8167
8168   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8169   SaveTape_VERS(file, &tape);
8170
8171   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8172   SaveTape_HEAD(file, &tape);
8173
8174   putFileChunkBE(file, "INFO", info_chunk_size);
8175   SaveTape_INFO(file, &tape);
8176
8177   putFileChunkBE(file, "BODY", body_chunk_size);
8178   SaveTape_BODY(file, &tape);
8179
8180   fclose(file);
8181
8182   SetFilePermissions(filename, PERMS_PRIVATE);
8183 }
8184
8185 void SaveTape(int nr)
8186 {
8187   char *filename = getTapeFilename(nr);
8188   int i;
8189
8190   InitTapeDirectory(leveldir_current->subdir);
8191
8192   tape.file_version = FILE_VERSION_ACTUAL;
8193   tape.game_version = GAME_VERSION_ACTUAL;
8194
8195   tape.num_participating_players = 0;
8196
8197   // count number of participating players
8198   for (i = 0; i < MAX_PLAYERS; i++)
8199     if (tape.player_participates[i])
8200       tape.num_participating_players++;
8201
8202   SaveTapeToFilename(filename);
8203
8204   tape.changed = FALSE;
8205 }
8206
8207 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8208                                   unsigned int req_state_added)
8209 {
8210   char *filename = getTapeFilename(nr);
8211   boolean new_tape = !fileExists(filename);
8212   boolean tape_saved = FALSE;
8213
8214   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8215   {
8216     SaveTape(nr);
8217
8218     if (new_tape)
8219       Request(msg_saved, REQ_CONFIRM | req_state_added);
8220
8221     tape_saved = TRUE;
8222   }
8223
8224   return tape_saved;
8225 }
8226
8227 boolean SaveTapeChecked(int nr)
8228 {
8229   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8230 }
8231
8232 boolean SaveTapeChecked_LevelSolved(int nr)
8233 {
8234   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8235                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
8236 }
8237
8238 void DumpTape(struct TapeInfo *tape)
8239 {
8240   int tape_frame_counter;
8241   int i, j;
8242
8243   if (tape->no_valid_file)
8244   {
8245     Error(ERR_WARN, "cannot dump -- no valid tape file found");
8246
8247     return;
8248   }
8249
8250   PrintLine("-", 79);
8251   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8252         tape->level_nr, tape->file_version, tape->game_version);
8253   Print("                  (effective engine version %08d)\n",
8254         tape->engine_version);
8255   Print("Level series identifier: '%s'\n", tape->level_identifier);
8256   PrintLine("-", 79);
8257
8258   tape_frame_counter = 0;
8259
8260   for (i = 0; i < tape->length; i++)
8261   {
8262     if (i >= MAX_TAPE_LEN)
8263       break;
8264
8265     Print("%04d: ", i);
8266
8267     for (j = 0; j < MAX_PLAYERS; j++)
8268     {
8269       if (tape->player_participates[j])
8270       {
8271         int action = tape->pos[i].action[j];
8272
8273         Print("%d:%02x ", j, action);
8274         Print("[%c%c%c%c|%c%c] - ",
8275               (action & JOY_LEFT ? '<' : ' '),
8276               (action & JOY_RIGHT ? '>' : ' '),
8277               (action & JOY_UP ? '^' : ' '),
8278               (action & JOY_DOWN ? 'v' : ' '),
8279               (action & JOY_BUTTON_1 ? '1' : ' '),
8280               (action & JOY_BUTTON_2 ? '2' : ' '));
8281       }
8282     }
8283
8284     Print("(%03d) ", tape->pos[i].delay);
8285     Print("[%05d]\n", tape_frame_counter);
8286
8287     tape_frame_counter += tape->pos[i].delay;
8288   }
8289
8290   PrintLine("-", 79);
8291 }
8292
8293
8294 // ============================================================================
8295 // score file functions
8296 // ============================================================================
8297
8298 void LoadScore(int nr)
8299 {
8300   int i;
8301   char *filename = getScoreFilename(nr);
8302   char cookie[MAX_LINE_LEN];
8303   char line[MAX_LINE_LEN];
8304   char *line_ptr;
8305   FILE *file;
8306
8307   // always start with reliable default values
8308   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8309   {
8310     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8311     highscore[i].Score = 0;
8312   }
8313
8314   if (!(file = fopen(filename, MODE_READ)))
8315     return;
8316
8317   // check file identifier
8318   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8319     cookie[0] = '\0';
8320   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8321     cookie[strlen(cookie) - 1] = '\0';
8322
8323   if (!checkCookieString(cookie, SCORE_COOKIE))
8324   {
8325     Error(ERR_WARN, "unknown format of score file '%s'", filename);
8326     fclose(file);
8327     return;
8328   }
8329
8330   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8331   {
8332     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8333       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8334     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8335       line[0] = '\0';
8336
8337     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8338       line[strlen(line) - 1] = '\0';
8339
8340     for (line_ptr = line; *line_ptr; line_ptr++)
8341     {
8342       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8343       {
8344         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8345         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8346         break;
8347       }
8348     }
8349   }
8350
8351   fclose(file);
8352 }
8353
8354 void SaveScore(int nr)
8355 {
8356   int i;
8357   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8358   char *filename = getScoreFilename(nr);
8359   FILE *file;
8360
8361   // used instead of "leveldir_current->subdir" (for network games)
8362   InitScoreDirectory(levelset.identifier);
8363
8364   if (!(file = fopen(filename, MODE_WRITE)))
8365   {
8366     Error(ERR_WARN, "cannot save score for level %d", nr);
8367     return;
8368   }
8369
8370   fprintf(file, "%s\n\n", SCORE_COOKIE);
8371
8372   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8373     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8374
8375   fclose(file);
8376
8377   SetFilePermissions(filename, permissions);
8378 }
8379
8380
8381 // ============================================================================
8382 // setup file functions
8383 // ============================================================================
8384
8385 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8386
8387
8388 static struct TokenInfo global_setup_tokens[] =
8389 {
8390   {
8391     TYPE_STRING,
8392     &setup.player_name,                         "player_name"
8393   },
8394   {
8395     TYPE_SWITCH,
8396     &setup.sound,                               "sound"
8397   },
8398   {
8399     TYPE_SWITCH,
8400     &setup.sound_loops,                         "repeating_sound_loops"
8401   },
8402   {
8403     TYPE_SWITCH,
8404     &setup.sound_music,                         "background_music"
8405   },
8406   {
8407     TYPE_SWITCH,
8408     &setup.sound_simple,                        "simple_sound_effects"
8409   },
8410   {
8411     TYPE_SWITCH,
8412     &setup.toons,                               "toons"
8413   },
8414   {
8415     TYPE_SWITCH,
8416     &setup.scroll_delay,                        "scroll_delay"
8417   },
8418   {
8419     TYPE_SWITCH,
8420     &setup.forced_scroll_delay,                 "forced_scroll_delay"
8421   },
8422   {
8423     TYPE_INTEGER,
8424     &setup.scroll_delay_value,                  "scroll_delay_value"
8425   },
8426   {
8427     TYPE_STRING,
8428     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
8429   },
8430   {
8431     TYPE_INTEGER,
8432     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
8433   },
8434   {
8435     TYPE_SWITCH,
8436     &setup.fade_screens,                        "fade_screens"
8437   },
8438   {
8439     TYPE_SWITCH,
8440     &setup.autorecord,                          "automatic_tape_recording"
8441   },
8442   {
8443     TYPE_SWITCH,
8444     &setup.show_titlescreen,                    "show_titlescreen"
8445   },
8446   {
8447     TYPE_SWITCH,
8448     &setup.quick_doors,                         "quick_doors"
8449   },
8450   {
8451     TYPE_SWITCH,
8452     &setup.team_mode,                           "team_mode"
8453   },
8454   {
8455     TYPE_SWITCH,
8456     &setup.handicap,                            "handicap"
8457   },
8458   {
8459     TYPE_SWITCH,
8460     &setup.skip_levels,                         "skip_levels"
8461   },
8462   {
8463     TYPE_SWITCH,
8464     &setup.increment_levels,                    "increment_levels"
8465   },
8466   {
8467     TYPE_SWITCH,
8468     &setup.auto_play_next_level,                "auto_play_next_level"
8469   },
8470   {
8471     TYPE_SWITCH,
8472     &setup.skip_scores_after_game,              "skip_scores_after_game"
8473   },
8474   {
8475     TYPE_SWITCH,
8476     &setup.time_limit,                          "time_limit"
8477   },
8478   {
8479     TYPE_SWITCH,
8480     &setup.fullscreen,                          "fullscreen"
8481   },
8482   {
8483     TYPE_INTEGER,
8484     &setup.window_scaling_percent,              "window_scaling_percent"
8485   },
8486   {
8487     TYPE_STRING,
8488     &setup.window_scaling_quality,              "window_scaling_quality"
8489   },
8490   {
8491     TYPE_STRING,
8492     &setup.screen_rendering_mode,               "screen_rendering_mode"
8493   },
8494   {
8495     TYPE_STRING,
8496     &setup.vsync_mode,                          "vsync_mode"
8497   },
8498   {
8499     TYPE_SWITCH,
8500     &setup.ask_on_escape,                       "ask_on_escape"
8501   },
8502   {
8503     TYPE_SWITCH,
8504     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
8505   },
8506   {
8507     TYPE_SWITCH,
8508     &setup.ask_on_game_over,                    "ask_on_game_over"
8509   },
8510   {
8511     TYPE_SWITCH,
8512     &setup.quick_switch,                        "quick_player_switch"
8513   },
8514   {
8515     TYPE_SWITCH,
8516     &setup.input_on_focus,                      "input_on_focus"
8517   },
8518   {
8519     TYPE_SWITCH,
8520     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
8521   },
8522   {
8523     TYPE_SWITCH,
8524     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
8525   },
8526   {
8527     TYPE_SWITCH,
8528     &setup.game_speed_extended,                 "game_speed_extended"
8529   },
8530   {
8531     TYPE_INTEGER,
8532     &setup.game_frame_delay,                    "game_frame_delay"
8533   },
8534   {
8535     TYPE_SWITCH,
8536     &setup.sp_show_border_elements,             "sp_show_border_elements"
8537   },
8538   {
8539     TYPE_SWITCH,
8540     &setup.small_game_graphics,                 "small_game_graphics"
8541   },
8542   {
8543     TYPE_SWITCH,
8544     &setup.show_snapshot_buttons,               "show_snapshot_buttons"
8545   },
8546   {
8547     TYPE_STRING,
8548     &setup.graphics_set,                        "graphics_set"
8549   },
8550   {
8551     TYPE_STRING,
8552     &setup.sounds_set,                          "sounds_set"
8553   },
8554   {
8555     TYPE_STRING,
8556     &setup.music_set,                           "music_set"
8557   },
8558   {
8559     TYPE_SWITCH3,
8560     &setup.override_level_graphics,             "override_level_graphics"
8561   },
8562   {
8563     TYPE_SWITCH3,
8564     &setup.override_level_sounds,               "override_level_sounds"
8565   },
8566   {
8567     TYPE_SWITCH3,
8568     &setup.override_level_music,                "override_level_music"
8569   },
8570   {
8571     TYPE_INTEGER,
8572     &setup.volume_simple,                       "volume_simple"
8573   },
8574   {
8575     TYPE_INTEGER,
8576     &setup.volume_loops,                        "volume_loops"
8577   },
8578   {
8579     TYPE_INTEGER,
8580     &setup.volume_music,                        "volume_music"
8581   },
8582   {
8583     TYPE_SWITCH,
8584     &setup.network_mode,                        "network_mode"
8585   },
8586   {
8587     TYPE_PLAYER,
8588     &setup.network_player_nr,                   "network_player"
8589   },
8590   {
8591     TYPE_STRING,
8592     &setup.network_server_hostname,             "network_server_hostname"
8593   },
8594   {
8595     TYPE_STRING,
8596     &setup.touch.control_type,                  "touch.control_type"
8597   },
8598   {
8599     TYPE_INTEGER,
8600     &setup.touch.move_distance,                 "touch.move_distance"
8601   },
8602   {
8603     TYPE_INTEGER,
8604     &setup.touch.drop_distance,                 "touch.drop_distance"
8605   },
8606   {
8607     TYPE_INTEGER,
8608     &setup.touch.transparency,                  "touch.transparency"
8609   },
8610   {
8611     TYPE_INTEGER,
8612     &setup.touch.draw_outlined,                 "touch.draw_outlined"
8613   },
8614   {
8615     TYPE_INTEGER,
8616     &setup.touch.draw_pressed,                  "touch.draw_pressed"
8617   },
8618   {
8619     TYPE_INTEGER,
8620     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
8621   },
8622   {
8623     TYPE_INTEGER,
8624     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
8625   },
8626   {
8627     TYPE_INTEGER,
8628     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
8629   },
8630   {
8631     TYPE_INTEGER,
8632     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
8633   },
8634 };
8635
8636 static struct TokenInfo auto_setup_tokens[] =
8637 {
8638   {
8639     TYPE_INTEGER,
8640     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
8641   },
8642 };
8643
8644 static struct TokenInfo editor_setup_tokens[] =
8645 {
8646   {
8647     TYPE_SWITCH,
8648     &setup.editor.el_classic,                   "editor.el_classic"
8649   },
8650   {
8651     TYPE_SWITCH,
8652     &setup.editor.el_custom,                    "editor.el_custom"
8653   },
8654   {
8655     TYPE_SWITCH,
8656     &setup.editor.el_user_defined,              "editor.el_user_defined"
8657   },
8658   {
8659     TYPE_SWITCH,
8660     &setup.editor.el_dynamic,                   "editor.el_dynamic"
8661   },
8662   {
8663     TYPE_SWITCH,
8664     &setup.editor.el_headlines,                 "editor.el_headlines"
8665   },
8666   {
8667     TYPE_SWITCH,
8668     &setup.editor.show_element_token,           "editor.show_element_token"
8669   },
8670 };
8671
8672 static struct TokenInfo editor_cascade_setup_tokens[] =
8673 {
8674   {
8675     TYPE_SWITCH,
8676     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
8677   },
8678   {
8679     TYPE_SWITCH,
8680     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
8681   },
8682   {
8683     TYPE_SWITCH,
8684     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
8685   },
8686   {
8687     TYPE_SWITCH,
8688     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
8689   },
8690   {
8691     TYPE_SWITCH,
8692     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
8693   },
8694   {
8695     TYPE_SWITCH,
8696     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
8697   },
8698   {
8699     TYPE_SWITCH,
8700     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
8701   },
8702   {
8703     TYPE_SWITCH,
8704     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
8705   },
8706   {
8707     TYPE_SWITCH,
8708     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
8709   },
8710   {
8711     TYPE_SWITCH,
8712     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
8713   },
8714   {
8715     TYPE_SWITCH,
8716     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
8717   },
8718   {
8719     TYPE_SWITCH,
8720     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
8721   },
8722   {
8723     TYPE_SWITCH,
8724     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
8725   },
8726   {
8727     TYPE_SWITCH,
8728     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
8729   },
8730   {
8731     TYPE_SWITCH,
8732     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
8733   },
8734   {
8735     TYPE_SWITCH,
8736     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
8737   },
8738   {
8739     TYPE_SWITCH,
8740     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
8741   },
8742 };
8743
8744 static struct TokenInfo shortcut_setup_tokens[] =
8745 {
8746   {
8747     TYPE_KEY_X11,
8748     &setup.shortcut.save_game,                  "shortcut.save_game"
8749   },
8750   {
8751     TYPE_KEY_X11,
8752     &setup.shortcut.load_game,                  "shortcut.load_game"
8753   },
8754   {
8755     TYPE_KEY_X11,
8756     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
8757   },
8758   {
8759     TYPE_KEY_X11,
8760     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
8761   },
8762   {
8763     TYPE_KEY_X11,
8764     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
8765   },
8766   {
8767     TYPE_KEY_X11,
8768     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
8769   },
8770   {
8771     TYPE_KEY_X11,
8772     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
8773   },
8774   {
8775     TYPE_KEY_X11,
8776     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
8777   },
8778   {
8779     TYPE_KEY_X11,
8780     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
8781   },
8782   {
8783     TYPE_KEY_X11,
8784     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
8785   },
8786   {
8787     TYPE_KEY_X11,
8788     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
8789   },
8790   {
8791     TYPE_KEY_X11,
8792     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
8793   },
8794   {
8795     TYPE_KEY_X11,
8796     &setup.shortcut.tape_record,                "shortcut.tape_record"
8797   },
8798   {
8799     TYPE_KEY_X11,
8800     &setup.shortcut.tape_play,                  "shortcut.tape_play"
8801   },
8802   {
8803     TYPE_KEY_X11,
8804     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
8805   },
8806   {
8807     TYPE_KEY_X11,
8808     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
8809   },
8810   {
8811     TYPE_KEY_X11,
8812     &setup.shortcut.sound_music,                "shortcut.sound_music"
8813   },
8814   {
8815     TYPE_KEY_X11,
8816     &setup.shortcut.snap_left,                  "shortcut.snap_left"
8817   },
8818   {
8819     TYPE_KEY_X11,
8820     &setup.shortcut.snap_right,                 "shortcut.snap_right"
8821   },
8822   {
8823     TYPE_KEY_X11,
8824     &setup.shortcut.snap_up,                    "shortcut.snap_up"
8825   },
8826   {
8827     TYPE_KEY_X11,
8828     &setup.shortcut.snap_down,                  "shortcut.snap_down"
8829   },
8830 };
8831
8832 static struct SetupInputInfo setup_input;
8833 static struct TokenInfo player_setup_tokens[] =
8834 {
8835   {
8836     TYPE_BOOLEAN,
8837     &setup_input.use_joystick,                  ".use_joystick"
8838   },
8839   {
8840     TYPE_STRING,
8841     &setup_input.joy.device_name,               ".joy.device_name"
8842   },
8843   {
8844     TYPE_INTEGER,
8845     &setup_input.joy.xleft,                     ".joy.xleft"
8846   },
8847   {
8848     TYPE_INTEGER,
8849     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
8850   },
8851   {
8852     TYPE_INTEGER,
8853     &setup_input.joy.xright,                    ".joy.xright"
8854   },
8855   {
8856     TYPE_INTEGER,
8857     &setup_input.joy.yupper,                    ".joy.yupper"
8858   },
8859   {
8860     TYPE_INTEGER,
8861     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
8862   },
8863   {
8864     TYPE_INTEGER,
8865     &setup_input.joy.ylower,                    ".joy.ylower"
8866   },
8867   {
8868     TYPE_INTEGER,
8869     &setup_input.joy.snap,                      ".joy.snap_field"
8870   },
8871   {
8872     TYPE_INTEGER,
8873     &setup_input.joy.drop,                      ".joy.place_bomb"
8874   },
8875   {
8876     TYPE_KEY_X11,
8877     &setup_input.key.left,                      ".key.move_left"
8878   },
8879   {
8880     TYPE_KEY_X11,
8881     &setup_input.key.right,                     ".key.move_right"
8882   },
8883   {
8884     TYPE_KEY_X11,
8885     &setup_input.key.up,                        ".key.move_up"
8886   },
8887   {
8888     TYPE_KEY_X11,
8889     &setup_input.key.down,                      ".key.move_down"
8890   },
8891   {
8892     TYPE_KEY_X11,
8893     &setup_input.key.snap,                      ".key.snap_field"
8894   },
8895   {
8896     TYPE_KEY_X11,
8897     &setup_input.key.drop,                      ".key.place_bomb"
8898   },
8899 };
8900
8901 static struct TokenInfo system_setup_tokens[] =
8902 {
8903   {
8904     TYPE_STRING,
8905     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
8906   },
8907   {
8908     TYPE_STRING,
8909     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
8910   },
8911   {
8912     TYPE_STRING,
8913     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
8914   },
8915   {
8916     TYPE_INTEGER,
8917     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
8918   },
8919 };
8920
8921 static struct TokenInfo internal_setup_tokens[] =
8922 {
8923   {
8924     TYPE_STRING,
8925     &setup.internal.program_title,              "program_title"
8926   },
8927   {
8928     TYPE_STRING,
8929     &setup.internal.program_version,            "program_version"
8930   },
8931   {
8932     TYPE_STRING,
8933     &setup.internal.program_author,             "program_author"
8934   },
8935   {
8936     TYPE_STRING,
8937     &setup.internal.program_email,              "program_email"
8938   },
8939   {
8940     TYPE_STRING,
8941     &setup.internal.program_website,            "program_website"
8942   },
8943   {
8944     TYPE_STRING,
8945     &setup.internal.program_copyright,          "program_copyright"
8946   },
8947   {
8948     TYPE_STRING,
8949     &setup.internal.program_company,            "program_company"
8950   },
8951   {
8952     TYPE_STRING,
8953     &setup.internal.program_icon_file,          "program_icon_file"
8954   },
8955   {
8956     TYPE_STRING,
8957     &setup.internal.default_graphics_set,       "default_graphics_set"
8958   },
8959   {
8960     TYPE_STRING,
8961     &setup.internal.default_sounds_set,         "default_sounds_set"
8962   },
8963   {
8964     TYPE_STRING,
8965     &setup.internal.default_music_set,          "default_music_set"
8966   },
8967   {
8968     TYPE_STRING,
8969     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
8970   },
8971   {
8972     TYPE_STRING,
8973     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
8974   },
8975   {
8976     TYPE_STRING,
8977     &setup.internal.fallback_music_file,        "fallback_music_file"
8978   },
8979   {
8980     TYPE_STRING,
8981     &setup.internal.default_level_series,       "default_level_series"
8982   },
8983   {
8984     TYPE_INTEGER,
8985     &setup.internal.default_window_width,       "default_window_width"
8986   },
8987   {
8988     TYPE_INTEGER,
8989     &setup.internal.default_window_height,      "default_window_height"
8990   },
8991   {
8992     TYPE_BOOLEAN,
8993     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
8994   },
8995   {
8996     TYPE_BOOLEAN,
8997     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
8998   },
8999   {
9000     TYPE_BOOLEAN,
9001     &setup.internal.create_user_levelset,       "create_user_levelset"
9002   },
9003   {
9004     TYPE_BOOLEAN,
9005     &setup.internal.menu_game,                  "menu_game"
9006   },
9007   {
9008     TYPE_BOOLEAN,
9009     &setup.internal.menu_editor,                "menu_editor"
9010   },
9011   {
9012     TYPE_BOOLEAN,
9013     &setup.internal.menu_graphics,              "menu_graphics"
9014   },
9015   {
9016     TYPE_BOOLEAN,
9017     &setup.internal.menu_sound,                 "menu_sound"
9018   },
9019   {
9020     TYPE_BOOLEAN,
9021     &setup.internal.menu_artwork,               "menu_artwork"
9022   },
9023   {
9024     TYPE_BOOLEAN,
9025     &setup.internal.menu_input,                 "menu_input"
9026   },
9027   {
9028     TYPE_BOOLEAN,
9029     &setup.internal.menu_touch,                 "menu_touch"
9030   },
9031   {
9032     TYPE_BOOLEAN,
9033     &setup.internal.menu_shortcuts,             "menu_shortcuts"
9034   },
9035   {
9036     TYPE_BOOLEAN,
9037     &setup.internal.menu_exit,                  "menu_exit"
9038   },
9039   {
9040     TYPE_BOOLEAN,
9041     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
9042   },
9043 };
9044
9045 static struct TokenInfo debug_setup_tokens[] =
9046 {
9047   {
9048     TYPE_INTEGER,
9049     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
9050   },
9051   {
9052     TYPE_INTEGER,
9053     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
9054   },
9055   {
9056     TYPE_INTEGER,
9057     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
9058   },
9059   {
9060     TYPE_INTEGER,
9061     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
9062   },
9063   {
9064     TYPE_INTEGER,
9065     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
9066   },
9067   {
9068     TYPE_INTEGER,
9069     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
9070   },
9071   {
9072     TYPE_INTEGER,
9073     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
9074   },
9075   {
9076     TYPE_INTEGER,
9077     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
9078   },
9079   {
9080     TYPE_INTEGER,
9081     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
9082   },
9083   {
9084     TYPE_INTEGER,
9085     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
9086   },
9087   {
9088     TYPE_KEY_X11,
9089     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
9090   },
9091   {
9092     TYPE_KEY_X11,
9093     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
9094   },
9095   {
9096     TYPE_KEY_X11,
9097     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
9098   },
9099   {
9100     TYPE_KEY_X11,
9101     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
9102   },
9103   {
9104     TYPE_KEY_X11,
9105     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
9106   },
9107   {
9108     TYPE_KEY_X11,
9109     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
9110   },
9111   {
9112     TYPE_KEY_X11,
9113     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
9114   },
9115   {
9116     TYPE_KEY_X11,
9117     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
9118   },
9119   {
9120     TYPE_KEY_X11,
9121     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
9122   },
9123   {
9124     TYPE_KEY_X11,
9125     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
9126   },
9127   {
9128     TYPE_BOOLEAN,
9129     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
9130   {
9131     TYPE_BOOLEAN,
9132     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
9133   },
9134   {
9135     TYPE_BOOLEAN,
9136     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
9137   },
9138 };
9139
9140 static struct TokenInfo options_setup_tokens[] =
9141 {
9142   {
9143     TYPE_BOOLEAN,
9144     &setup.options.verbose,                     "options.verbose"
9145   },
9146 };
9147
9148 static char *get_corrected_login_name(char *login_name)
9149 {
9150   // needed because player name must be a fixed length string
9151   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
9152
9153   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
9154   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
9155
9156   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         // name has been cut
9157     if (strchr(login_name_new, ' '))
9158       *strchr(login_name_new, ' ') = '\0';
9159
9160   return login_name_new;
9161 }
9162
9163 static void setSetupInfoToDefaults(struct SetupInfo *si)
9164 {
9165   int i;
9166
9167   si->player_name = get_corrected_login_name(getLoginName());
9168
9169   si->sound = TRUE;
9170   si->sound_loops = TRUE;
9171   si->sound_music = TRUE;
9172   si->sound_simple = TRUE;
9173   si->toons = TRUE;
9174   si->scroll_delay = TRUE;
9175   si->forced_scroll_delay = FALSE;
9176   si->scroll_delay_value = STD_SCROLL_DELAY;
9177   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9178   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9179   si->fade_screens = TRUE;
9180   si->autorecord = TRUE;
9181   si->show_titlescreen = TRUE;
9182   si->quick_doors = FALSE;
9183   si->team_mode = FALSE;
9184   si->handicap = TRUE;
9185   si->skip_levels = TRUE;
9186   si->increment_levels = TRUE;
9187   si->auto_play_next_level = TRUE;
9188   si->skip_scores_after_game = FALSE;
9189   si->time_limit = TRUE;
9190   si->fullscreen = FALSE;
9191   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9192   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9193   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9194   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9195   si->ask_on_escape = TRUE;
9196   si->ask_on_escape_editor = TRUE;
9197   si->ask_on_game_over = TRUE;
9198   si->quick_switch = FALSE;
9199   si->input_on_focus = FALSE;
9200   si->prefer_aga_graphics = TRUE;
9201   si->prefer_lowpass_sounds = FALSE;
9202   si->game_speed_extended = FALSE;
9203   si->game_frame_delay = GAME_FRAME_DELAY;
9204   si->sp_show_border_elements = FALSE;
9205   si->small_game_graphics = FALSE;
9206   si->show_snapshot_buttons = FALSE;
9207
9208   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9209   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
9210   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
9211
9212   si->override_level_graphics = FALSE;
9213   si->override_level_sounds = FALSE;
9214   si->override_level_music = FALSE;
9215
9216   si->volume_simple = 100;              // percent
9217   si->volume_loops = 100;               // percent
9218   si->volume_music = 100;               // percent
9219
9220   si->network_mode = FALSE;
9221   si->network_player_nr = 0;            // first player
9222   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9223
9224   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9225   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
9226   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
9227   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
9228   si->touch.draw_outlined = TRUE;
9229   si->touch.draw_pressed = TRUE;
9230
9231   for (i = 0; i < 2; i++)
9232   {
9233     char *default_grid_button[6][2] =
9234     {
9235       { "      ", "  ^^  " },
9236       { "      ", "  ^^  " },
9237       { "      ", "<<  >>" },
9238       { "      ", "<<  >>" },
9239       { "111222", "  vv  " },
9240       { "111222", "  vv  " }
9241     };
9242     int grid_xsize = DEFAULT_GRID_XSIZE(i);
9243     int grid_ysize = DEFAULT_GRID_YSIZE(i);
9244     int min_xsize = MIN(6, grid_xsize);
9245     int min_ysize = MIN(6, grid_ysize);
9246     int startx = grid_xsize - min_xsize;
9247     int starty = grid_ysize - min_ysize;
9248     int x, y;
9249
9250     // virtual buttons grid can only be set to defaults if video is initialized
9251     // (this will be repeated if virtual buttons are not loaded from setup file)
9252     if (video.initialized)
9253     {
9254       si->touch.grid_xsize[i] = grid_xsize;
9255       si->touch.grid_ysize[i] = grid_ysize;
9256     }
9257     else
9258     {
9259       si->touch.grid_xsize[i] = -1;
9260       si->touch.grid_ysize[i] = -1;
9261     }
9262
9263     for (x = 0; x < MAX_GRID_XSIZE; x++)
9264       for (y = 0; y < MAX_GRID_YSIZE; y++)
9265         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9266
9267     for (x = 0; x < min_xsize; x++)
9268       for (y = 0; y < min_ysize; y++)
9269         si->touch.grid_button[i][x][starty + y] =
9270           default_grid_button[y][0][x];
9271
9272     for (x = 0; x < min_xsize; x++)
9273       for (y = 0; y < min_ysize; y++)
9274         si->touch.grid_button[i][startx + x][starty + y] =
9275           default_grid_button[y][1][x];
9276   }
9277
9278   si->touch.grid_initialized            = video.initialized;
9279
9280   si->editor.el_boulderdash             = TRUE;
9281   si->editor.el_emerald_mine            = TRUE;
9282   si->editor.el_emerald_mine_club       = TRUE;
9283   si->editor.el_more                    = TRUE;
9284   si->editor.el_sokoban                 = TRUE;
9285   si->editor.el_supaplex                = TRUE;
9286   si->editor.el_diamond_caves           = TRUE;
9287   si->editor.el_dx_boulderdash          = TRUE;
9288
9289   si->editor.el_mirror_magic            = TRUE;
9290   si->editor.el_deflektor               = TRUE;
9291
9292   si->editor.el_chars                   = TRUE;
9293   si->editor.el_steel_chars             = TRUE;
9294
9295   si->editor.el_classic                 = TRUE;
9296   si->editor.el_custom                  = TRUE;
9297
9298   si->editor.el_user_defined            = FALSE;
9299   si->editor.el_dynamic                 = TRUE;
9300
9301   si->editor.el_headlines               = TRUE;
9302
9303   si->editor.show_element_token         = FALSE;
9304
9305   si->editor.use_template_for_new_levels = TRUE;
9306
9307   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
9308   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
9309   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
9310
9311   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
9312   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
9313   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
9314   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
9315   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9316
9317   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
9318   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
9319   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
9320   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
9321   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
9322   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
9323
9324   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
9325   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
9326   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
9327
9328   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
9329   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
9330   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
9331   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
9332
9333   for (i = 0; i < MAX_PLAYERS; i++)
9334   {
9335     si->input[i].use_joystick = FALSE;
9336     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9337     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
9338     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9339     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
9340     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
9341     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9342     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
9343     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
9344     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
9345     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
9346     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9347     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
9348     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
9349     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
9350     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
9351   }
9352
9353   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9354   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9355   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9356   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9357
9358   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
9359   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
9360   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
9361   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
9362   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
9363   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9364   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
9365
9366   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9367
9368   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9369   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
9370   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
9371
9372   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9373   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
9374   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
9375
9376   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9377   si->internal.choose_from_top_leveldir = FALSE;
9378   si->internal.show_scaling_in_title = TRUE;
9379   si->internal.create_user_levelset = TRUE;
9380
9381   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
9382   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9383
9384   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9385   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9386   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9387   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9388   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9389   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9390   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9391   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9392   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9393   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9394
9395   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9396   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9397   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9398   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9399   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9400   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9401   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9402   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9403   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9404   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9405
9406   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9407   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
9408
9409   si->debug.show_frames_per_second = FALSE;
9410
9411   si->options.verbose = FALSE;
9412
9413 #if defined(PLATFORM_ANDROID)
9414   si->fullscreen = TRUE;
9415 #endif
9416 }
9417
9418 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9419 {
9420   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9421 }
9422
9423 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9424 {
9425   si->editor_cascade.el_bd              = TRUE;
9426   si->editor_cascade.el_em              = TRUE;
9427   si->editor_cascade.el_emc             = TRUE;
9428   si->editor_cascade.el_rnd             = TRUE;
9429   si->editor_cascade.el_sb              = TRUE;
9430   si->editor_cascade.el_sp              = TRUE;
9431   si->editor_cascade.el_dc              = TRUE;
9432   si->editor_cascade.el_dx              = TRUE;
9433
9434   si->editor_cascade.el_mm              = TRUE;
9435   si->editor_cascade.el_df              = TRUE;
9436
9437   si->editor_cascade.el_chars           = FALSE;
9438   si->editor_cascade.el_steel_chars     = FALSE;
9439   si->editor_cascade.el_ce              = FALSE;
9440   si->editor_cascade.el_ge              = FALSE;
9441   si->editor_cascade.el_ref             = FALSE;
9442   si->editor_cascade.el_user            = FALSE;
9443   si->editor_cascade.el_dynamic         = FALSE;
9444 }
9445
9446 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
9447
9448 static char *getHideSetupToken(void *setup_value)
9449 {
9450   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9451
9452   if (setup_value != NULL)
9453     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9454
9455   return hide_setup_token;
9456 }
9457
9458 void setHideSetupEntry(void *setup_value)
9459 {
9460   char *hide_setup_token = getHideSetupToken(setup_value);
9461
9462   if (setup_value != NULL)
9463     setHashEntry(hide_setup_hash, hide_setup_token, "");
9464 }
9465
9466 boolean hideSetupEntry(void *setup_value)
9467 {
9468   char *hide_setup_token = getHideSetupToken(setup_value);
9469
9470   return (setup_value != NULL &&
9471           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9472 }
9473
9474 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9475                                       struct TokenInfo *token_info,
9476                                       int token_nr, char *token_text)
9477 {
9478   char *token_hide_text = getStringCat2(token_text, ".hide");
9479   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9480
9481   // set the value of this setup option in the setup option structure
9482   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9483
9484   // check if this setup option should be hidden in the setup menu
9485   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9486     setHideSetupEntry(token_info[token_nr].value);
9487 }
9488
9489 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9490                                       struct TokenInfo *token_info,
9491                                       int token_nr)
9492 {
9493   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9494                             token_info[token_nr].text);
9495 }
9496
9497 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9498 {
9499   int i, pnr;
9500
9501   if (!setup_file_hash)
9502     return;
9503
9504   if (hide_setup_hash == NULL)
9505     hide_setup_hash = newSetupFileHash();
9506
9507   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9508     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9509
9510   setup.touch.grid_initialized = TRUE;
9511   for (i = 0; i < 2; i++)
9512   {
9513     int grid_xsize = setup.touch.grid_xsize[i];
9514     int grid_ysize = setup.touch.grid_ysize[i];
9515     int x, y;
9516
9517     // if virtual buttons are not loaded from setup file, repeat initializing
9518     // virtual buttons grid with default values later when video is initialized
9519     if (grid_xsize == -1 ||
9520         grid_ysize == -1)
9521     {
9522       setup.touch.grid_initialized = FALSE;
9523
9524       continue;
9525     }
9526
9527     for (y = 0; y < grid_ysize; y++)
9528     {
9529       char token_string[MAX_LINE_LEN];
9530
9531       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9532
9533       char *value_string = getHashEntry(setup_file_hash, token_string);
9534
9535       if (value_string == NULL)
9536         continue;
9537
9538       for (x = 0; x < grid_xsize; x++)
9539       {
9540         char c = value_string[x];
9541
9542         setup.touch.grid_button[i][x][y] =
9543           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9544       }
9545     }
9546   }
9547
9548   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9549     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9550
9551   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9552     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9553
9554   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9555   {
9556     char prefix[30];
9557
9558     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9559
9560     setup_input = setup.input[pnr];
9561     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9562     {
9563       char full_token[100];
9564
9565       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9566       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9567                                 full_token);
9568     }
9569     setup.input[pnr] = setup_input;
9570   }
9571
9572   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9573     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9574
9575   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9576     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9577
9578   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9579     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9580
9581   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9582     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9583
9584   setHideRelatedSetupEntries();
9585 }
9586
9587 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9588 {
9589   int i;
9590
9591   if (!setup_file_hash)
9592     return;
9593
9594   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9595     setSetupInfo(auto_setup_tokens, i,
9596                  getHashEntry(setup_file_hash,
9597                               auto_setup_tokens[i].text));
9598 }
9599
9600 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9601 {
9602   int i;
9603
9604   if (!setup_file_hash)
9605     return;
9606
9607   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9608     setSetupInfo(editor_cascade_setup_tokens, i,
9609                  getHashEntry(setup_file_hash,
9610                               editor_cascade_setup_tokens[i].text));
9611 }
9612
9613 void LoadSetupFromFilename(char *filename)
9614 {
9615   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9616
9617   if (setup_file_hash)
9618   {
9619     decodeSetupFileHash(setup_file_hash);
9620
9621     freeSetupFileHash(setup_file_hash);
9622   }
9623   else
9624   {
9625     Error(ERR_DEBUG, "using default setup values");
9626   }
9627 }
9628
9629 static void LoadSetup_SpecialPostProcessing(void)
9630 {
9631   char *player_name_new;
9632
9633   // needed to work around problems with fixed length strings
9634   player_name_new = get_corrected_login_name(setup.player_name);
9635   free(setup.player_name);
9636   setup.player_name = player_name_new;
9637
9638   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9639   if (setup.scroll_delay == FALSE)
9640   {
9641     setup.scroll_delay_value = MIN_SCROLL_DELAY;
9642     setup.scroll_delay = TRUE;                  // now always "on"
9643   }
9644
9645   // make sure that scroll delay value stays inside valid range
9646   setup.scroll_delay_value =
9647     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9648 }
9649
9650 void LoadSetup(void)
9651 {
9652   char *filename;
9653
9654   // always start with reliable default values
9655   setSetupInfoToDefaults(&setup);
9656
9657   // try to load setup values from default setup file
9658   filename = getDefaultSetupFilename();
9659
9660   if (fileExists(filename))
9661     LoadSetupFromFilename(filename);
9662
9663   // try to load setup values from user setup file
9664   filename = getSetupFilename();
9665
9666   LoadSetupFromFilename(filename);
9667
9668   LoadSetup_SpecialPostProcessing();
9669 }
9670
9671 void LoadSetup_AutoSetup(void)
9672 {
9673   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9674   SetupFileHash *setup_file_hash = NULL;
9675
9676   // always start with reliable default values
9677   setSetupInfoToDefaults_AutoSetup(&setup);
9678
9679   setup_file_hash = loadSetupFileHash(filename);
9680
9681   if (setup_file_hash)
9682   {
9683     decodeSetupFileHash_AutoSetup(setup_file_hash);
9684
9685     freeSetupFileHash(setup_file_hash);
9686   }
9687
9688   free(filename);
9689 }
9690
9691 void LoadSetup_EditorCascade(void)
9692 {
9693   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9694   SetupFileHash *setup_file_hash = NULL;
9695
9696   // always start with reliable default values
9697   setSetupInfoToDefaults_EditorCascade(&setup);
9698
9699   setup_file_hash = loadSetupFileHash(filename);
9700
9701   if (setup_file_hash)
9702   {
9703     decodeSetupFileHash_EditorCascade(setup_file_hash);
9704
9705     freeSetupFileHash(setup_file_hash);
9706   }
9707
9708   free(filename);
9709 }
9710
9711 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9712                                            char *mapping_line)
9713 {
9714   char mapping_guid[MAX_LINE_LEN];
9715   char *mapping_start, *mapping_end;
9716
9717   // get GUID from game controller mapping line: copy complete line
9718   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9719   mapping_guid[MAX_LINE_LEN - 1] = '\0';
9720
9721   // get GUID from game controller mapping line: cut after GUID part
9722   mapping_start = strchr(mapping_guid, ',');
9723   if (mapping_start != NULL)
9724     *mapping_start = '\0';
9725
9726   // cut newline from game controller mapping line
9727   mapping_end = strchr(mapping_line, '\n');
9728   if (mapping_end != NULL)
9729     *mapping_end = '\0';
9730
9731   // add mapping entry to game controller mappings hash
9732   setHashEntry(mappings_hash, mapping_guid, mapping_line);
9733 }
9734
9735 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9736                                                  char *filename)
9737 {
9738   FILE *file;
9739
9740   if (!(file = fopen(filename, MODE_READ)))
9741   {
9742     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9743
9744     return;
9745   }
9746
9747   while (!feof(file))
9748   {
9749     char line[MAX_LINE_LEN];
9750
9751     if (!fgets(line, MAX_LINE_LEN, file))
9752       break;
9753
9754     addGameControllerMappingToHash(mappings_hash, line);
9755   }
9756
9757   fclose(file);
9758 }
9759
9760 void SaveSetup(void)
9761 {
9762   char *filename = getSetupFilename();
9763   FILE *file;
9764   int i, pnr;
9765
9766   InitUserDataDirectory();
9767
9768   if (!(file = fopen(filename, MODE_WRITE)))
9769   {
9770     Error(ERR_WARN, "cannot write setup file '%s'", filename);
9771     return;
9772   }
9773
9774   fprintFileHeader(file, SETUP_FILENAME);
9775
9776   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9777   {
9778     // just to make things nicer :)
9779     if (global_setup_tokens[i].value == &setup.sound                    ||
9780         global_setup_tokens[i].value == &setup.graphics_set             ||
9781         global_setup_tokens[i].value == &setup.volume_simple            ||
9782         global_setup_tokens[i].value == &setup.network_mode             ||
9783         global_setup_tokens[i].value == &setup.touch.control_type       ||
9784         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
9785         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9786       fprintf(file, "\n");
9787
9788     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9789   }
9790
9791   for (i = 0; i < 2; i++)
9792   {
9793     int grid_xsize = setup.touch.grid_xsize[i];
9794     int grid_ysize = setup.touch.grid_ysize[i];
9795     int x, y;
9796
9797     fprintf(file, "\n");
9798
9799     for (y = 0; y < grid_ysize; y++)
9800     {
9801       char token_string[MAX_LINE_LEN];
9802       char value_string[MAX_LINE_LEN];
9803
9804       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9805
9806       for (x = 0; x < grid_xsize; x++)
9807       {
9808         char c = setup.touch.grid_button[i][x][y];
9809
9810         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9811       }
9812
9813       value_string[grid_xsize] = '\0';
9814
9815       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9816     }
9817   }
9818
9819   fprintf(file, "\n");
9820   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9821     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9822
9823   fprintf(file, "\n");
9824   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9825     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9826
9827   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9828   {
9829     char prefix[30];
9830
9831     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9832     fprintf(file, "\n");
9833
9834     setup_input = setup.input[pnr];
9835     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9836       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9837   }
9838
9839   fprintf(file, "\n");
9840   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9841     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9842
9843   // (internal setup values not saved to user setup file)
9844
9845   fprintf(file, "\n");
9846   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9847     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9848
9849   fprintf(file, "\n");
9850   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9851     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9852
9853   fclose(file);
9854
9855   SetFilePermissions(filename, PERMS_PRIVATE);
9856 }
9857
9858 void SaveSetup_AutoSetup(void)
9859 {
9860   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9861   FILE *file;
9862   int i;
9863
9864   InitUserDataDirectory();
9865
9866   if (!(file = fopen(filename, MODE_WRITE)))
9867   {
9868     Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9869     free(filename);
9870     return;
9871   }
9872
9873   fprintFileHeader(file, AUTOSETUP_FILENAME);
9874
9875   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9876     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9877
9878   fclose(file);
9879
9880   SetFilePermissions(filename, PERMS_PRIVATE);
9881
9882   free(filename);
9883 }
9884
9885 void SaveSetup_EditorCascade(void)
9886 {
9887   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9888   FILE *file;
9889   int i;
9890
9891   InitUserDataDirectory();
9892
9893   if (!(file = fopen(filename, MODE_WRITE)))
9894   {
9895     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9896     free(filename);
9897     return;
9898   }
9899
9900   fprintFileHeader(file, EDITORCASCADE_FILENAME);
9901
9902   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9903     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9904
9905   fclose(file);
9906
9907   SetFilePermissions(filename, PERMS_PRIVATE);
9908
9909   free(filename);
9910 }
9911
9912 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9913                                                   char *filename)
9914 {
9915   FILE *file;
9916
9917   if (!(file = fopen(filename, MODE_WRITE)))
9918   {
9919     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9920
9921     return;
9922   }
9923
9924   BEGIN_HASH_ITERATION(mappings_hash, itr)
9925   {
9926     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9927   }
9928   END_HASH_ITERATION(mappings_hash, itr)
9929
9930   fclose(file);
9931 }
9932
9933 void SaveSetup_AddGameControllerMapping(char *mapping)
9934 {
9935   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9936   SetupFileHash *mappings_hash = newSetupFileHash();
9937
9938   InitUserDataDirectory();
9939
9940   // load existing personal game controller mappings
9941   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9942
9943   // add new mapping to personal game controller mappings
9944   addGameControllerMappingToHash(mappings_hash, mapping);
9945
9946   // save updated personal game controller mappings
9947   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9948
9949   freeSetupFileHash(mappings_hash);
9950   free(filename);
9951 }
9952
9953 void LoadCustomElementDescriptions(void)
9954 {
9955   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9956   SetupFileHash *setup_file_hash;
9957   int i;
9958
9959   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9960   {
9961     if (element_info[i].custom_description != NULL)
9962     {
9963       free(element_info[i].custom_description);
9964       element_info[i].custom_description = NULL;
9965     }
9966   }
9967
9968   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9969     return;
9970
9971   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9972   {
9973     char *token = getStringCat2(element_info[i].token_name, ".name");
9974     char *value = getHashEntry(setup_file_hash, token);
9975
9976     if (value != NULL)
9977       element_info[i].custom_description = getStringCopy(value);
9978
9979     free(token);
9980   }
9981
9982   freeSetupFileHash(setup_file_hash);
9983 }
9984
9985 static int getElementFromToken(char *token)
9986 {
9987   char *value = getHashEntry(element_token_hash, token);
9988
9989   if (value != NULL)
9990     return atoi(value);
9991
9992   Error(ERR_WARN, "unknown element token '%s'", token);
9993
9994   return EL_UNDEFINED;
9995 }
9996
9997 void FreeGlobalAnimEventInfo(void)
9998 {
9999   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10000
10001   if (gaei->event_list == NULL)
10002     return;
10003
10004   int i;
10005
10006   for (i = 0; i < gaei->num_event_lists; i++)
10007   {
10008     checked_free(gaei->event_list[i]->event_value);
10009     checked_free(gaei->event_list[i]);
10010   }
10011
10012   checked_free(gaei->event_list);
10013
10014   gaei->event_list = NULL;
10015   gaei->num_event_lists = 0;
10016 }
10017
10018 static int AddGlobalAnimEventList(void)
10019 {
10020   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10021   int list_pos = gaei->num_event_lists++;
10022
10023   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10024                                      sizeof(struct GlobalAnimEventListInfo *));
10025
10026   gaei->event_list[list_pos] =
10027     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10028
10029   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10030
10031   gaeli->event_value = NULL;
10032   gaeli->num_event_values = 0;
10033
10034   return list_pos;
10035 }
10036
10037 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10038 {
10039   // do not add empty global animation events
10040   if (event_value == ANIM_EVENT_NONE)
10041     return list_pos;
10042
10043   // if list position is undefined, create new list
10044   if (list_pos == ANIM_EVENT_UNDEFINED)
10045     list_pos = AddGlobalAnimEventList();
10046
10047   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10048   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10049   int value_pos = gaeli->num_event_values++;
10050
10051   gaeli->event_value = checked_realloc(gaeli->event_value,
10052                                        gaeli->num_event_values * sizeof(int *));
10053
10054   gaeli->event_value[value_pos] = event_value;
10055
10056   return list_pos;
10057 }
10058
10059 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10060 {
10061   if (list_pos == ANIM_EVENT_UNDEFINED)
10062     return ANIM_EVENT_NONE;
10063
10064   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10065   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10066
10067   return gaeli->event_value[value_pos];
10068 }
10069
10070 int GetGlobalAnimEventValueCount(int list_pos)
10071 {
10072   if (list_pos == ANIM_EVENT_UNDEFINED)
10073     return 0;
10074
10075   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10076   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10077
10078   return gaeli->num_event_values;
10079 }
10080
10081 // This function checks if a string <s> of the format "string1, string2, ..."
10082 // exactly contains a string <s_contained>.
10083
10084 static boolean string_has_parameter(char *s, char *s_contained)
10085 {
10086   char *substring;
10087
10088   if (s == NULL || s_contained == NULL)
10089     return FALSE;
10090
10091   if (strlen(s_contained) > strlen(s))
10092     return FALSE;
10093
10094   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10095   {
10096     char next_char = s[strlen(s_contained)];
10097
10098     // check if next character is delimiter or whitespace
10099     return (next_char == ',' || next_char == '\0' ||
10100             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10101   }
10102
10103   // check if string contains another parameter string after a comma
10104   substring = strchr(s, ',');
10105   if (substring == NULL)        // string does not contain a comma
10106     return FALSE;
10107
10108   // advance string pointer to next character after the comma
10109   substring++;
10110
10111   // skip potential whitespaces after the comma
10112   while (*substring == ' ' || *substring == '\t')
10113     substring++;
10114
10115   return string_has_parameter(substring, s_contained);
10116 }
10117
10118 static int get_anim_parameter_value(char *s)
10119 {
10120   int event_value[] =
10121   {
10122     ANIM_EVENT_CLICK,
10123     ANIM_EVENT_INIT,
10124     ANIM_EVENT_START,
10125     ANIM_EVENT_END,
10126     ANIM_EVENT_POST
10127   };
10128   char *pattern_1[] =
10129   {
10130     "click:anim_",
10131     "init:anim_",
10132     "start:anim_",
10133     "end:anim_",
10134     "post:anim_"
10135   };
10136   char *pattern_2 = ".part_";
10137   char *matching_char = NULL;
10138   char *s_ptr = s;
10139   int pattern_1_len = 0;
10140   int result = ANIM_EVENT_NONE;
10141   int i;
10142
10143   for (i = 0; i < ARRAY_SIZE(event_value); i++)
10144   {
10145     matching_char = strstr(s_ptr, pattern_1[i]);
10146     pattern_1_len = strlen(pattern_1[i]);
10147     result = event_value[i];
10148
10149     if (matching_char != NULL)
10150       break;
10151   }
10152
10153   if (matching_char == NULL)
10154     return ANIM_EVENT_NONE;
10155
10156   s_ptr = matching_char + pattern_1_len;
10157
10158   // check for main animation number ("anim_X" or "anim_XX")
10159   if (*s_ptr >= '0' && *s_ptr <= '9')
10160   {
10161     int gic_anim_nr = (*s_ptr++ - '0');
10162
10163     if (*s_ptr >= '0' && *s_ptr <= '9')
10164       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10165
10166     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10167       return ANIM_EVENT_NONE;
10168
10169     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10170   }
10171   else
10172   {
10173     // invalid main animation number specified
10174
10175     return ANIM_EVENT_NONE;
10176   }
10177
10178   // check for animation part number ("part_X" or "part_XX") (optional)
10179   if (strPrefix(s_ptr, pattern_2))
10180   {
10181     s_ptr += strlen(pattern_2);
10182
10183     if (*s_ptr >= '0' && *s_ptr <= '9')
10184     {
10185       int gic_part_nr = (*s_ptr++ - '0');
10186
10187       if (*s_ptr >= '0' && *s_ptr <= '9')
10188         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10189
10190       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10191         return ANIM_EVENT_NONE;
10192
10193       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10194     }
10195     else
10196     {
10197       // invalid animation part number specified
10198
10199       return ANIM_EVENT_NONE;
10200     }
10201   }
10202
10203   // discard result if next character is neither delimiter nor whitespace
10204   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10205         *s_ptr == ' ' || *s_ptr == '\t'))
10206     return ANIM_EVENT_NONE;
10207
10208   return result;
10209 }
10210
10211 static int get_anim_parameter_values(char *s)
10212 {
10213   int list_pos = ANIM_EVENT_UNDEFINED;
10214   int event_value = ANIM_EVENT_DEFAULT;
10215
10216   if (string_has_parameter(s, "any"))
10217     event_value |= ANIM_EVENT_ANY;
10218
10219   if (string_has_parameter(s, "click:self") ||
10220       string_has_parameter(s, "click") ||
10221       string_has_parameter(s, "self"))
10222     event_value |= ANIM_EVENT_SELF;
10223
10224   if (string_has_parameter(s, "unclick:any"))
10225     event_value |= ANIM_EVENT_UNCLICK_ANY;
10226
10227   // if animation event found, add it to global animation event list
10228   if (event_value != ANIM_EVENT_NONE)
10229     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10230
10231   while (s != NULL)
10232   {
10233     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10234     event_value = get_anim_parameter_value(s);
10235
10236     // if animation event found, add it to global animation event list
10237     if (event_value != ANIM_EVENT_NONE)
10238       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10239
10240     // continue with next part of the string, starting with next comma
10241     s = strchr(s + 1, ',');
10242   }
10243
10244   return list_pos;
10245 }
10246
10247 static int get_anim_action_parameter_value(char *token)
10248 {
10249   int result = getImageIDFromToken(token);
10250
10251   if (result == -1)
10252   {
10253     char *gfx_token = getStringCat2("gfx.", token);
10254
10255     result = getImageIDFromToken(gfx_token);
10256
10257     checked_free(gfx_token);
10258   }
10259
10260   if (result == -1)
10261   {
10262     Key key = getKeyFromX11KeyName(token);
10263
10264     if (key != KSYM_UNDEFINED)
10265       result = -(int)key;
10266   }
10267
10268   if (result == -1)
10269     result = ANIM_EVENT_ACTION_NONE;
10270
10271   return result;
10272 }
10273
10274 int get_parameter_value(char *value_raw, char *suffix, int type)
10275 {
10276   char *value = getStringToLower(value_raw);
10277   int result = 0;       // probably a save default value
10278
10279   if (strEqual(suffix, ".direction"))
10280   {
10281     result = (strEqual(value, "left")  ? MV_LEFT :
10282               strEqual(value, "right") ? MV_RIGHT :
10283               strEqual(value, "up")    ? MV_UP :
10284               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
10285   }
10286   else if (strEqual(suffix, ".position"))
10287   {
10288     result = (strEqual(value, "left")   ? POS_LEFT :
10289               strEqual(value, "right")  ? POS_RIGHT :
10290               strEqual(value, "top")    ? POS_TOP :
10291               strEqual(value, "upper")  ? POS_UPPER :
10292               strEqual(value, "middle") ? POS_MIDDLE :
10293               strEqual(value, "lower")  ? POS_LOWER :
10294               strEqual(value, "bottom") ? POS_BOTTOM :
10295               strEqual(value, "any")    ? POS_ANY :
10296               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
10297   }
10298   else if (strEqual(suffix, ".align"))
10299   {
10300     result = (strEqual(value, "left")   ? ALIGN_LEFT :
10301               strEqual(value, "right")  ? ALIGN_RIGHT :
10302               strEqual(value, "center") ? ALIGN_CENTER :
10303               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10304   }
10305   else if (strEqual(suffix, ".valign"))
10306   {
10307     result = (strEqual(value, "top")    ? VALIGN_TOP :
10308               strEqual(value, "bottom") ? VALIGN_BOTTOM :
10309               strEqual(value, "middle") ? VALIGN_MIDDLE :
10310               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10311   }
10312   else if (strEqual(suffix, ".anim_mode"))
10313   {
10314     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
10315               string_has_parameter(value, "loop")       ? ANIM_LOOP :
10316               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
10317               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
10318               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
10319               string_has_parameter(value, "random")     ? ANIM_RANDOM :
10320               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
10321               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
10322               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
10323               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10324               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
10325               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
10326               string_has_parameter(value, "all")        ? ANIM_ALL :
10327               ANIM_DEFAULT);
10328
10329     if (string_has_parameter(value, "once"))
10330       result |= ANIM_ONCE;
10331
10332     if (string_has_parameter(value, "reverse"))
10333       result |= ANIM_REVERSE;
10334
10335     if (string_has_parameter(value, "opaque_player"))
10336       result |= ANIM_OPAQUE_PLAYER;
10337
10338     if (string_has_parameter(value, "static_panel"))
10339       result |= ANIM_STATIC_PANEL;
10340   }
10341   else if (strEqual(suffix, ".init_event") ||
10342            strEqual(suffix, ".anim_event"))
10343   {
10344     result = get_anim_parameter_values(value);
10345   }
10346   else if (strEqual(suffix, ".init_delay_action") ||
10347            strEqual(suffix, ".anim_delay_action") ||
10348            strEqual(suffix, ".post_delay_action") ||
10349            strEqual(suffix, ".init_event_action") ||
10350            strEqual(suffix, ".anim_event_action"))
10351   {
10352     result = get_anim_action_parameter_value(value_raw);
10353   }
10354   else if (strEqual(suffix, ".class"))
10355   {
10356     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10357               get_hash_from_key(value));
10358   }
10359   else if (strEqual(suffix, ".style"))
10360   {
10361     result = STYLE_DEFAULT;
10362
10363     if (string_has_parameter(value, "accurate_borders"))
10364       result |= STYLE_ACCURATE_BORDERS;
10365
10366     if (string_has_parameter(value, "inner_corners"))
10367       result |= STYLE_INNER_CORNERS;
10368
10369     if (string_has_parameter(value, "reverse"))
10370       result |= STYLE_REVERSE;
10371
10372     if (string_has_parameter(value, "block_clicks"))
10373       result |= STYLE_BLOCK;
10374
10375     if (string_has_parameter(value, "passthrough_clicks"))
10376       result |= STYLE_PASSTHROUGH;
10377
10378     if (string_has_parameter(value, "multiple_actions"))
10379       result |= STYLE_MULTIPLE_ACTIONS;
10380   }
10381   else if (strEqual(suffix, ".fade_mode"))
10382   {
10383     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
10384               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
10385               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
10386               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
10387               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
10388               FADE_MODE_DEFAULT);
10389   }
10390   else if (strEqual(suffix, ".auto_delay_unit"))
10391   {
10392     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
10393               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10394               AUTO_DELAY_UNIT_DEFAULT);
10395   }
10396   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
10397   {
10398     result = gfx.get_font_from_token_function(value);
10399   }
10400   else          // generic parameter of type integer or boolean
10401   {
10402     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10403               type == TYPE_INTEGER ? get_integer_from_string(value) :
10404               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10405               ARG_UNDEFINED_VALUE);
10406   }
10407
10408   free(value);
10409
10410   return result;
10411 }
10412
10413 static int get_token_parameter_value(char *token, char *value_raw)
10414 {
10415   char *suffix;
10416
10417   if (token == NULL || value_raw == NULL)
10418     return ARG_UNDEFINED_VALUE;
10419
10420   suffix = strrchr(token, '.');
10421   if (suffix == NULL)
10422     suffix = token;
10423
10424   if (strEqual(suffix, ".element"))
10425     return getElementFromToken(value_raw);
10426
10427   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10428   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10429 }
10430
10431 void InitMenuDesignSettings_Static(void)
10432 {
10433   int i;
10434
10435   // always start with reliable default values from static default config
10436   for (i = 0; image_config_vars[i].token != NULL; i++)
10437   {
10438     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10439
10440     if (value != NULL)
10441       *image_config_vars[i].value =
10442         get_token_parameter_value(image_config_vars[i].token, value);
10443   }
10444 }
10445
10446 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10447 {
10448   int i;
10449
10450   // the following initializes hierarchical values from static configuration
10451
10452   // special case: initialize "ARG_DEFAULT" values in static default config
10453   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10454   titlescreen_initial_first_default.fade_mode  =
10455     title_initial_first_default.fade_mode;
10456   titlescreen_initial_first_default.fade_delay =
10457     title_initial_first_default.fade_delay;
10458   titlescreen_initial_first_default.post_delay =
10459     title_initial_first_default.post_delay;
10460   titlescreen_initial_first_default.auto_delay =
10461     title_initial_first_default.auto_delay;
10462   titlescreen_initial_first_default.auto_delay_unit =
10463     title_initial_first_default.auto_delay_unit;
10464   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
10465   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10466   titlescreen_first_default.post_delay = title_first_default.post_delay;
10467   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10468   titlescreen_first_default.auto_delay_unit =
10469     title_first_default.auto_delay_unit;
10470   titlemessage_initial_first_default.fade_mode  =
10471     title_initial_first_default.fade_mode;
10472   titlemessage_initial_first_default.fade_delay =
10473     title_initial_first_default.fade_delay;
10474   titlemessage_initial_first_default.post_delay =
10475     title_initial_first_default.post_delay;
10476   titlemessage_initial_first_default.auto_delay =
10477     title_initial_first_default.auto_delay;
10478   titlemessage_initial_first_default.auto_delay_unit =
10479     title_initial_first_default.auto_delay_unit;
10480   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
10481   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10482   titlemessage_first_default.post_delay = title_first_default.post_delay;
10483   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10484   titlemessage_first_default.auto_delay_unit =
10485     title_first_default.auto_delay_unit;
10486
10487   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
10488   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10489   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10490   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10491   titlescreen_initial_default.auto_delay_unit =
10492     title_initial_default.auto_delay_unit;
10493   titlescreen_default.fade_mode  = title_default.fade_mode;
10494   titlescreen_default.fade_delay = title_default.fade_delay;
10495   titlescreen_default.post_delay = title_default.post_delay;
10496   titlescreen_default.auto_delay = title_default.auto_delay;
10497   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10498   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
10499   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10500   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10501   titlemessage_initial_default.auto_delay_unit =
10502     title_initial_default.auto_delay_unit;
10503   titlemessage_default.fade_mode  = title_default.fade_mode;
10504   titlemessage_default.fade_delay = title_default.fade_delay;
10505   titlemessage_default.post_delay = title_default.post_delay;
10506   titlemessage_default.auto_delay = title_default.auto_delay;
10507   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10508
10509   // special case: initialize "ARG_DEFAULT" values in static default config
10510   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10511   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10512   {
10513     titlescreen_initial_first[i] = titlescreen_initial_first_default;
10514     titlescreen_first[i] = titlescreen_first_default;
10515     titlemessage_initial_first[i] = titlemessage_initial_first_default;
10516     titlemessage_first[i] = titlemessage_first_default;
10517
10518     titlescreen_initial[i] = titlescreen_initial_default;
10519     titlescreen[i] = titlescreen_default;
10520     titlemessage_initial[i] = titlemessage_initial_default;
10521     titlemessage[i] = titlemessage_default;
10522   }
10523
10524   // special case: initialize "ARG_DEFAULT" values in static default config
10525   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10526   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10527   {
10528     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
10529       continue;
10530
10531     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10532     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10533     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10534   }
10535
10536   // special case: initialize "ARG_DEFAULT" values in static default config
10537   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10538   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10539   {
10540     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10541     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10542     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10543
10544     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
10545       continue;
10546
10547     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10548   }
10549 }
10550
10551 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10552 {
10553   static struct
10554   {
10555     struct XY *dst, *src;
10556   }
10557   game_buttons_xy[] =
10558   {
10559     { &game.button.save,        &game.button.stop       },
10560     { &game.button.pause2,      &game.button.pause      },
10561     { &game.button.load,        &game.button.play       },
10562     { &game.button.undo,        &game.button.stop       },
10563     { &game.button.redo,        &game.button.play       },
10564
10565     { NULL,                     NULL                    }
10566   };
10567   int i, j;
10568
10569   // special case: initialize later added SETUP list size from LEVELS value
10570   if (menu.list_size[GAME_MODE_SETUP] == -1)
10571     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10572
10573   // set default position for snapshot buttons to stop/pause/play buttons
10574   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10575     if ((*game_buttons_xy[i].dst).x == -1 &&
10576         (*game_buttons_xy[i].dst).y == -1)
10577       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10578
10579   // --------------------------------------------------------------------------
10580   // dynamic viewports (including playfield margins, borders and alignments)
10581   // --------------------------------------------------------------------------
10582
10583   // dynamic viewports currently only supported for landscape mode
10584   int display_width  = MAX(video.display_width, video.display_height);
10585   int display_height = MIN(video.display_width, video.display_height);
10586
10587   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10588   {
10589     struct RectWithBorder *vp_window    = &viewport.window[i];
10590     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10591     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
10592     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
10593     boolean dynamic_window_width     = (vp_window->min_width     != -1);
10594     boolean dynamic_window_height    = (vp_window->min_height    != -1);
10595     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
10596     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10597
10598     // adjust window size if min/max width/height is specified
10599
10600     if (vp_window->min_width != -1)
10601     {
10602       int window_width = display_width;
10603
10604       // when using static window height, use aspect ratio of display
10605       if (vp_window->min_height == -1)
10606         window_width = vp_window->height * display_width / display_height;
10607
10608       vp_window->width = MAX(vp_window->min_width, window_width);
10609     }
10610
10611     if (vp_window->min_height != -1)
10612     {
10613       int window_height = display_height;
10614
10615       // when using static window width, use aspect ratio of display
10616       if (vp_window->min_width == -1)
10617         window_height = vp_window->width * display_height / display_width;
10618
10619       vp_window->height = MAX(vp_window->min_height, window_height);
10620     }
10621
10622     if (vp_window->max_width != -1)
10623       vp_window->width = MIN(vp_window->width, vp_window->max_width);
10624
10625     if (vp_window->max_height != -1)
10626       vp_window->height = MIN(vp_window->height, vp_window->max_height);
10627
10628     int playfield_width  = vp_window->width;
10629     int playfield_height = vp_window->height;
10630
10631     // adjust playfield size and position according to specified margins
10632
10633     playfield_width  -= vp_playfield->margin_left;
10634     playfield_width  -= vp_playfield->margin_right;
10635
10636     playfield_height -= vp_playfield->margin_top;
10637     playfield_height -= vp_playfield->margin_bottom;
10638
10639     // adjust playfield size if min/max width/height is specified
10640
10641     if (vp_playfield->min_width != -1)
10642       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10643
10644     if (vp_playfield->min_height != -1)
10645       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10646
10647     if (vp_playfield->max_width != -1)
10648       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10649
10650     if (vp_playfield->max_height != -1)
10651       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10652
10653     // adjust playfield position according to specified alignment
10654
10655     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10656       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10657     else if (vp_playfield->align == ALIGN_CENTER)
10658       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10659     else if (vp_playfield->align == ALIGN_RIGHT)
10660       vp_playfield->x += playfield_width - vp_playfield->width;
10661
10662     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10663       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10664     else if (vp_playfield->valign == VALIGN_MIDDLE)
10665       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10666     else if (vp_playfield->valign == VALIGN_BOTTOM)
10667       vp_playfield->y += playfield_height - vp_playfield->height;
10668
10669     vp_playfield->x += vp_playfield->margin_left;
10670     vp_playfield->y += vp_playfield->margin_top;
10671
10672     // adjust individual playfield borders if only default border is specified
10673
10674     if (vp_playfield->border_left == -1)
10675       vp_playfield->border_left = vp_playfield->border_size;
10676     if (vp_playfield->border_right == -1)
10677       vp_playfield->border_right = vp_playfield->border_size;
10678     if (vp_playfield->border_top == -1)
10679       vp_playfield->border_top = vp_playfield->border_size;
10680     if (vp_playfield->border_bottom == -1)
10681       vp_playfield->border_bottom = vp_playfield->border_size;
10682
10683     // set dynamic playfield borders if borders are specified as undefined
10684     // (but only if window size was dynamic and playfield size was static)
10685
10686     if (dynamic_window_width && !dynamic_playfield_width)
10687     {
10688       if (vp_playfield->border_left == -1)
10689       {
10690         vp_playfield->border_left = (vp_playfield->x -
10691                                      vp_playfield->margin_left);
10692         vp_playfield->x     -= vp_playfield->border_left;
10693         vp_playfield->width += vp_playfield->border_left;
10694       }
10695
10696       if (vp_playfield->border_right == -1)
10697       {
10698         vp_playfield->border_right = (vp_window->width -
10699                                       vp_playfield->x -
10700                                       vp_playfield->width -
10701                                       vp_playfield->margin_right);
10702         vp_playfield->width += vp_playfield->border_right;
10703       }
10704     }
10705
10706     if (dynamic_window_height && !dynamic_playfield_height)
10707     {
10708       if (vp_playfield->border_top == -1)
10709       {
10710         vp_playfield->border_top = (vp_playfield->y -
10711                                     vp_playfield->margin_top);
10712         vp_playfield->y      -= vp_playfield->border_top;
10713         vp_playfield->height += vp_playfield->border_top;
10714       }
10715
10716       if (vp_playfield->border_bottom == -1)
10717       {
10718         vp_playfield->border_bottom = (vp_window->height -
10719                                        vp_playfield->y -
10720                                        vp_playfield->height -
10721                                        vp_playfield->margin_bottom);
10722         vp_playfield->height += vp_playfield->border_bottom;
10723       }
10724     }
10725
10726     // adjust playfield size to be a multiple of a defined alignment tile size
10727
10728     int align_size = vp_playfield->align_size;
10729     int playfield_xtiles = vp_playfield->width  / align_size;
10730     int playfield_ytiles = vp_playfield->height / align_size;
10731     int playfield_width_corrected  = playfield_xtiles * align_size;
10732     int playfield_height_corrected = playfield_ytiles * align_size;
10733     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10734                                  i == GFX_SPECIAL_ARG_EDITOR);
10735
10736     if (is_playfield_mode &&
10737         dynamic_playfield_width &&
10738         vp_playfield->width != playfield_width_corrected)
10739     {
10740       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10741
10742       vp_playfield->width = playfield_width_corrected;
10743
10744       if (vp_playfield->align == ALIGN_LEFT)
10745       {
10746         vp_playfield->border_left += playfield_xdiff;
10747       }
10748       else if (vp_playfield->align == ALIGN_RIGHT)
10749       {
10750         vp_playfield->border_right += playfield_xdiff;
10751       }
10752       else if (vp_playfield->align == ALIGN_CENTER)
10753       {
10754         int border_left_diff  = playfield_xdiff / 2;
10755         int border_right_diff = playfield_xdiff - border_left_diff;
10756
10757         vp_playfield->border_left  += border_left_diff;
10758         vp_playfield->border_right += border_right_diff;
10759       }
10760     }
10761
10762     if (is_playfield_mode &&
10763         dynamic_playfield_height &&
10764         vp_playfield->height != playfield_height_corrected)
10765     {
10766       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10767
10768       vp_playfield->height = playfield_height_corrected;
10769
10770       if (vp_playfield->valign == VALIGN_TOP)
10771       {
10772         vp_playfield->border_top += playfield_ydiff;
10773       }
10774       else if (vp_playfield->align == VALIGN_BOTTOM)
10775       {
10776         vp_playfield->border_right += playfield_ydiff;
10777       }
10778       else if (vp_playfield->align == VALIGN_MIDDLE)
10779       {
10780         int border_top_diff    = playfield_ydiff / 2;
10781         int border_bottom_diff = playfield_ydiff - border_top_diff;
10782
10783         vp_playfield->border_top    += border_top_diff;
10784         vp_playfield->border_bottom += border_bottom_diff;
10785       }
10786     }
10787
10788     // adjust door positions according to specified alignment
10789
10790     for (j = 0; j < 2; j++)
10791     {
10792       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10793
10794       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10795         vp_door->x = ALIGNED_VP_XPOS(vp_door);
10796       else if (vp_door->align == ALIGN_CENTER)
10797         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10798       else if (vp_door->align == ALIGN_RIGHT)
10799         vp_door->x += vp_window->width - vp_door->width;
10800
10801       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10802         vp_door->y = ALIGNED_VP_YPOS(vp_door);
10803       else if (vp_door->valign == VALIGN_MIDDLE)
10804         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10805       else if (vp_door->valign == VALIGN_BOTTOM)
10806         vp_door->y += vp_window->height - vp_door->height;
10807     }
10808   }
10809 }
10810
10811 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10812 {
10813   static struct
10814   {
10815     struct XYTileSize *dst, *src;
10816     int graphic;
10817   }
10818   editor_buttons_xy[] =
10819   {
10820     {
10821       &editor.button.element_left,      &editor.palette.element_left,
10822       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10823     },
10824     {
10825       &editor.button.element_middle,    &editor.palette.element_middle,
10826       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10827     },
10828     {
10829       &editor.button.element_right,     &editor.palette.element_right,
10830       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10831     },
10832
10833     { NULL,                     NULL                    }
10834   };
10835   int i;
10836
10837   // set default position for element buttons to element graphics
10838   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10839   {
10840     if ((*editor_buttons_xy[i].dst).x == -1 &&
10841         (*editor_buttons_xy[i].dst).y == -1)
10842     {
10843       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10844
10845       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10846
10847       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10848     }
10849   }
10850
10851   // adjust editor palette rows and columns if specified to be dynamic
10852
10853   if (editor.palette.cols == -1)
10854   {
10855     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10856     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10857     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10858
10859     editor.palette.cols = (vp_width - sc_width) / bt_width;
10860
10861     if (editor.palette.x == -1)
10862     {
10863       int palette_width = editor.palette.cols * bt_width + sc_width;
10864
10865       editor.palette.x = (vp_width - palette_width) / 2;
10866     }
10867   }
10868
10869   if (editor.palette.rows == -1)
10870   {
10871     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
10872     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
10873     int tx_height = getFontHeight(FONT_TEXT_2);
10874
10875     editor.palette.rows = (vp_height - tx_height) / bt_height;
10876
10877     if (editor.palette.y == -1)
10878     {
10879       int palette_height = editor.palette.rows * bt_height + tx_height;
10880
10881       editor.palette.y = (vp_height - palette_height) / 2;
10882     }
10883   }
10884 }
10885
10886 static void LoadMenuDesignSettingsFromFilename(char *filename)
10887 {
10888   static struct TitleFadingInfo tfi;
10889   static struct TitleMessageInfo tmi;
10890   static struct TokenInfo title_tokens[] =
10891   {
10892     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
10893     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
10894     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
10895     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
10896     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
10897
10898     { -1,               NULL,                   NULL                    }
10899   };
10900   static struct TokenInfo titlemessage_tokens[] =
10901   {
10902     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
10903     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
10904     { TYPE_INTEGER,     &tmi.width,             ".width"                },
10905     { TYPE_INTEGER,     &tmi.height,            ".height"               },
10906     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
10907     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
10908     { TYPE_INTEGER,     &tmi.align,             ".align"                },
10909     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
10910     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
10911     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
10912     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
10913     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
10914     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
10915     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
10916     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
10917     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
10918     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
10919     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
10920
10921     { -1,               NULL,                   NULL                    }
10922   };
10923   static struct
10924   {
10925     struct TitleFadingInfo *info;
10926     char *text;
10927   }
10928   title_info[] =
10929   {
10930     // initialize first titles from "enter screen" definitions, if defined
10931     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
10932     { &title_first_default,             "menu.enter_screen.TITLE"       },
10933
10934     // initialize title screens from "next screen" definitions, if defined
10935     { &title_initial_default,           "menu.next_screen.TITLE"        },
10936     { &title_default,                   "menu.next_screen.TITLE"        },
10937
10938     { NULL,                             NULL                            }
10939   };
10940   static struct
10941   {
10942     struct TitleMessageInfo *array;
10943     char *text;
10944   }
10945   titlemessage_arrays[] =
10946   {
10947     // initialize first titles from "enter screen" definitions, if defined
10948     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
10949     { titlescreen_first,                "menu.enter_screen.TITLE"       },
10950     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
10951     { titlemessage_first,               "menu.enter_screen.TITLE"       },
10952
10953     // initialize titles from "next screen" definitions, if defined
10954     { titlescreen_initial,              "menu.next_screen.TITLE"        },
10955     { titlescreen,                      "menu.next_screen.TITLE"        },
10956     { titlemessage_initial,             "menu.next_screen.TITLE"        },
10957     { titlemessage,                     "menu.next_screen.TITLE"        },
10958
10959     // overwrite titles with title definitions, if defined
10960     { titlescreen_initial_first,        "[title_initial]"               },
10961     { titlescreen_first,                "[title]"                       },
10962     { titlemessage_initial_first,       "[title_initial]"               },
10963     { titlemessage_first,               "[title]"                       },
10964
10965     { titlescreen_initial,              "[title_initial]"               },
10966     { titlescreen,                      "[title]"                       },
10967     { titlemessage_initial,             "[title_initial]"               },
10968     { titlemessage,                     "[title]"                       },
10969
10970     // overwrite titles with title screen/message definitions, if defined
10971     { titlescreen_initial_first,        "[titlescreen_initial]"         },
10972     { titlescreen_first,                "[titlescreen]"                 },
10973     { titlemessage_initial_first,       "[titlemessage_initial]"        },
10974     { titlemessage_first,               "[titlemessage]"                },
10975
10976     { titlescreen_initial,              "[titlescreen_initial]"         },
10977     { titlescreen,                      "[titlescreen]"                 },
10978     { titlemessage_initial,             "[titlemessage_initial]"        },
10979     { titlemessage,                     "[titlemessage]"                },
10980
10981     { NULL,                             NULL                            }
10982   };
10983   SetupFileHash *setup_file_hash;
10984   int i, j, k;
10985
10986   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10987     return;
10988
10989   // the following initializes hierarchical values from dynamic configuration
10990
10991   // special case: initialize with default values that may be overwritten
10992   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
10993   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10994   {
10995     struct TokenIntPtrInfo menu_config[] =
10996     {
10997       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
10998       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
10999       { "menu.list_size",       &menu.list_size[i]      }
11000     };
11001
11002     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11003     {
11004       char *token = menu_config[j].token;
11005       char *value = getHashEntry(setup_file_hash, token);
11006
11007       if (value != NULL)
11008         *menu_config[j].value = get_integer_from_string(value);
11009     }
11010   }
11011
11012   // special case: initialize with default values that may be overwritten
11013   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11014   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11015   {
11016     struct TokenIntPtrInfo menu_config[] =
11017     {
11018       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
11019       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
11020       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
11021     };
11022
11023     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11024     {
11025       char *token = menu_config[j].token;
11026       char *value = getHashEntry(setup_file_hash, token);
11027
11028       if (value != NULL)
11029         *menu_config[j].value = get_integer_from_string(value);
11030     }
11031   }
11032
11033   // special case: initialize with default values that may be overwritten
11034   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11035   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11036   {
11037     struct TokenIntPtrInfo menu_config[] =
11038     {
11039       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
11040       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
11041     };
11042
11043     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11044     {
11045       char *token = menu_config[j].token;
11046       char *value = getHashEntry(setup_file_hash, token);
11047
11048       if (value != NULL)
11049         *menu_config[j].value = get_integer_from_string(value);
11050     }
11051   }
11052
11053   // special case: initialize with default values that may be overwritten
11054   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11055   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11056   {
11057     struct TokenIntPtrInfo menu_config[] =
11058     {
11059       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
11060       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
11061       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
11062       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
11063       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
11064       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
11065       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
11066       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
11067       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
11068     };
11069
11070     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11071     {
11072       char *token = menu_config[j].token;
11073       char *value = getHashEntry(setup_file_hash, token);
11074
11075       if (value != NULL)
11076         *menu_config[j].value = get_integer_from_string(value);
11077     }
11078   }
11079
11080   // special case: initialize with default values that may be overwritten
11081   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11082   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11083   {
11084     struct TokenIntPtrInfo menu_config[] =
11085     {
11086       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
11087       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11088       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11089       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
11090       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11091       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11092       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
11093       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
11094       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
11095     };
11096
11097     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11098     {
11099       char *token = menu_config[j].token;
11100       char *value = getHashEntry(setup_file_hash, token);
11101
11102       if (value != NULL)
11103         *menu_config[j].value = get_token_parameter_value(token, value);
11104     }
11105   }
11106
11107   // special case: initialize with default values that may be overwritten
11108   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11109   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11110   {
11111     struct
11112     {
11113       char *token_prefix;
11114       struct RectWithBorder *struct_ptr;
11115     }
11116     vp_struct[] =
11117     {
11118       { "viewport.window",      &viewport.window[i]     },
11119       { "viewport.playfield",   &viewport.playfield[i]  },
11120       { "viewport.door_1",      &viewport.door_1[i]     },
11121       { "viewport.door_2",      &viewport.door_2[i]     }
11122     };
11123
11124     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11125     {
11126       struct TokenIntPtrInfo vp_config[] =
11127       {
11128         { ".x",                 &vp_struct[j].struct_ptr->x             },
11129         { ".y",                 &vp_struct[j].struct_ptr->y             },
11130         { ".width",             &vp_struct[j].struct_ptr->width         },
11131         { ".height",            &vp_struct[j].struct_ptr->height        },
11132         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
11133         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
11134         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
11135         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
11136         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
11137         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
11138         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
11139         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
11140         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
11141         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
11142         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
11143         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
11144         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
11145         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
11146         { ".align",             &vp_struct[j].struct_ptr->align         },
11147         { ".valign",            &vp_struct[j].struct_ptr->valign        }
11148       };
11149
11150       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11151       {
11152         char *token = getStringCat2(vp_struct[j].token_prefix,
11153                                     vp_config[k].token);
11154         char *value = getHashEntry(setup_file_hash, token);
11155
11156         if (value != NULL)
11157           *vp_config[k].value = get_token_parameter_value(token, value);
11158
11159         free(token);
11160       }
11161     }
11162   }
11163
11164   // special case: initialize with default values that may be overwritten
11165   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11166   for (i = 0; title_info[i].info != NULL; i++)
11167   {
11168     struct TitleFadingInfo *info = title_info[i].info;
11169     char *base_token = title_info[i].text;
11170
11171     for (j = 0; title_tokens[j].type != -1; j++)
11172     {
11173       char *token = getStringCat2(base_token, title_tokens[j].text);
11174       char *value = getHashEntry(setup_file_hash, token);
11175
11176       if (value != NULL)
11177       {
11178         int parameter_value = get_token_parameter_value(token, value);
11179
11180         tfi = *info;
11181
11182         *(int *)title_tokens[j].value = (int)parameter_value;
11183
11184         *info = tfi;
11185       }
11186
11187       free(token);
11188     }
11189   }
11190
11191   // special case: initialize with default values that may be overwritten
11192   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11193   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11194   {
11195     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11196     char *base_token = titlemessage_arrays[i].text;
11197
11198     for (j = 0; titlemessage_tokens[j].type != -1; j++)
11199     {
11200       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11201       char *value = getHashEntry(setup_file_hash, token);
11202
11203       if (value != NULL)
11204       {
11205         int parameter_value = get_token_parameter_value(token, value);
11206
11207         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11208         {
11209           tmi = array[k];
11210
11211           if (titlemessage_tokens[j].type == TYPE_INTEGER)
11212             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
11213           else
11214             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11215
11216           array[k] = tmi;
11217         }
11218       }
11219
11220       free(token);
11221     }
11222   }
11223
11224   // special case: check if network and preview player positions are redefined,
11225   // to compare this later against the main menu level preview being redefined
11226   struct TokenIntPtrInfo menu_config_players[] =
11227   {
11228     { "main.network_players.x", &menu.main.network_players.redefined    },
11229     { "main.network_players.y", &menu.main.network_players.redefined    },
11230     { "main.preview_players.x", &menu.main.preview_players.redefined    },
11231     { "main.preview_players.y", &menu.main.preview_players.redefined    },
11232     { "preview.x",              &preview.redefined                      },
11233     { "preview.y",              &preview.redefined                      }
11234   };
11235
11236   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11237     *menu_config_players[i].value = FALSE;
11238
11239   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11240     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11241       *menu_config_players[i].value = TRUE;
11242
11243   // read (and overwrite with) values that may be specified in config file
11244   for (i = 0; image_config_vars[i].token != NULL; i++)
11245   {
11246     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11247
11248     // (ignore definitions set to "[DEFAULT]" which are already initialized)
11249     if (value != NULL && !strEqual(value, ARG_DEFAULT))
11250       *image_config_vars[i].value =
11251         get_token_parameter_value(image_config_vars[i].token, value);
11252   }
11253
11254   freeSetupFileHash(setup_file_hash);
11255 }
11256
11257 void LoadMenuDesignSettings(void)
11258 {
11259   char *filename_base = UNDEFINED_FILENAME, *filename_local;
11260
11261   InitMenuDesignSettings_Static();
11262   InitMenuDesignSettings_SpecialPreProcessing();
11263
11264   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11265   {
11266     // first look for special settings configured in level series config
11267     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11268
11269     if (fileExists(filename_base))
11270       LoadMenuDesignSettingsFromFilename(filename_base);
11271   }
11272
11273   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11274
11275   if (filename_local != NULL && !strEqual(filename_base, filename_local))
11276     LoadMenuDesignSettingsFromFilename(filename_local);
11277
11278   InitMenuDesignSettings_SpecialPostProcessing();
11279 }
11280
11281 void LoadMenuDesignSettings_AfterGraphics(void)
11282 {
11283   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11284 }
11285
11286 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11287 {
11288   char *filename = getEditorSetupFilename();
11289   SetupFileList *setup_file_list, *list;
11290   SetupFileHash *element_hash;
11291   int num_unknown_tokens = 0;
11292   int i;
11293
11294   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11295     return;
11296
11297   element_hash = newSetupFileHash();
11298
11299   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11300     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11301
11302   // determined size may be larger than needed (due to unknown elements)
11303   *num_elements = 0;
11304   for (list = setup_file_list; list != NULL; list = list->next)
11305     (*num_elements)++;
11306
11307   // add space for up to 3 more elements for padding that may be needed
11308   *num_elements += 3;
11309
11310   // free memory for old list of elements, if needed
11311   checked_free(*elements);
11312
11313   // allocate memory for new list of elements
11314   *elements = checked_malloc(*num_elements * sizeof(int));
11315
11316   *num_elements = 0;
11317   for (list = setup_file_list; list != NULL; list = list->next)
11318   {
11319     char *value = getHashEntry(element_hash, list->token);
11320
11321     if (value == NULL)          // try to find obsolete token mapping
11322     {
11323       char *mapped_token = get_mapped_token(list->token);
11324
11325       if (mapped_token != NULL)
11326       {
11327         value = getHashEntry(element_hash, mapped_token);
11328
11329         free(mapped_token);
11330       }
11331     }
11332
11333     if (value != NULL)
11334     {
11335       (*elements)[(*num_elements)++] = atoi(value);
11336     }
11337     else
11338     {
11339       if (num_unknown_tokens == 0)
11340       {
11341         Error(ERR_INFO_LINE, "-");
11342         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11343         Error(ERR_INFO, "- config file: '%s'", filename);
11344
11345         num_unknown_tokens++;
11346       }
11347
11348       Error(ERR_INFO, "- token: '%s'", list->token);
11349     }
11350   }
11351
11352   if (num_unknown_tokens > 0)
11353     Error(ERR_INFO_LINE, "-");
11354
11355   while (*num_elements % 4)     // pad with empty elements, if needed
11356     (*elements)[(*num_elements)++] = EL_EMPTY;
11357
11358   freeSetupFileList(setup_file_list);
11359   freeSetupFileHash(element_hash);
11360
11361 #if 0
11362   for (i = 0; i < *num_elements; i++)
11363     printf("editor: element '%s' [%d]\n",
11364            element_info[(*elements)[i]].token_name, (*elements)[i]);
11365 #endif
11366 }
11367
11368 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11369                                                      boolean is_sound)
11370 {
11371   SetupFileHash *setup_file_hash = NULL;
11372   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11373   char *filename_music, *filename_prefix, *filename_info;
11374   struct
11375   {
11376     char *token;
11377     char **value_ptr;
11378   }
11379   token_to_value_ptr[] =
11380   {
11381     { "title_header",   &tmp_music_file_info.title_header       },
11382     { "artist_header",  &tmp_music_file_info.artist_header      },
11383     { "album_header",   &tmp_music_file_info.album_header       },
11384     { "year_header",    &tmp_music_file_info.year_header        },
11385
11386     { "title",          &tmp_music_file_info.title              },
11387     { "artist",         &tmp_music_file_info.artist             },
11388     { "album",          &tmp_music_file_info.album              },
11389     { "year",           &tmp_music_file_info.year               },
11390
11391     { NULL,             NULL                                    },
11392   };
11393   int i;
11394
11395   filename_music = (is_sound ? getCustomSoundFilename(basename) :
11396                     getCustomMusicFilename(basename));
11397
11398   if (filename_music == NULL)
11399     return NULL;
11400
11401   // ---------- try to replace file extension ----------
11402
11403   filename_prefix = getStringCopy(filename_music);
11404   if (strrchr(filename_prefix, '.') != NULL)
11405     *strrchr(filename_prefix, '.') = '\0';
11406   filename_info = getStringCat2(filename_prefix, ".txt");
11407
11408   if (fileExists(filename_info))
11409     setup_file_hash = loadSetupFileHash(filename_info);
11410
11411   free(filename_prefix);
11412   free(filename_info);
11413
11414   if (setup_file_hash == NULL)
11415   {
11416     // ---------- try to add file extension ----------
11417
11418     filename_prefix = getStringCopy(filename_music);
11419     filename_info = getStringCat2(filename_prefix, ".txt");
11420
11421     if (fileExists(filename_info))
11422       setup_file_hash = loadSetupFileHash(filename_info);
11423
11424     free(filename_prefix);
11425     free(filename_info);
11426   }
11427
11428   if (setup_file_hash == NULL)
11429     return NULL;
11430
11431   // ---------- music file info found ----------
11432
11433   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11434
11435   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11436   {
11437     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11438
11439     *token_to_value_ptr[i].value_ptr =
11440       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11441   }
11442
11443   tmp_music_file_info.basename = getStringCopy(basename);
11444   tmp_music_file_info.music = music;
11445   tmp_music_file_info.is_sound = is_sound;
11446
11447   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11448   *new_music_file_info = tmp_music_file_info;
11449
11450   return new_music_file_info;
11451 }
11452
11453 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11454 {
11455   return get_music_file_info_ext(basename, music, FALSE);
11456 }
11457
11458 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11459 {
11460   return get_music_file_info_ext(basename, sound, TRUE);
11461 }
11462
11463 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11464                                      char *basename, boolean is_sound)
11465 {
11466   for (; list != NULL; list = list->next)
11467     if (list->is_sound == is_sound && strEqual(list->basename, basename))
11468       return TRUE;
11469
11470   return FALSE;
11471 }
11472
11473 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11474 {
11475   return music_info_listed_ext(list, basename, FALSE);
11476 }
11477
11478 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11479 {
11480   return music_info_listed_ext(list, basename, TRUE);
11481 }
11482
11483 void LoadMusicInfo(void)
11484 {
11485   char *music_directory = getCustomMusicDirectory();
11486   int num_music = getMusicListSize();
11487   int num_music_noconf = 0;
11488   int num_sounds = getSoundListSize();
11489   Directory *dir;
11490   DirectoryEntry *dir_entry;
11491   struct FileInfo *music, *sound;
11492   struct MusicFileInfo *next, **new;
11493   int i;
11494
11495   while (music_file_info != NULL)
11496   {
11497     next = music_file_info->next;
11498
11499     checked_free(music_file_info->basename);
11500
11501     checked_free(music_file_info->title_header);
11502     checked_free(music_file_info->artist_header);
11503     checked_free(music_file_info->album_header);
11504     checked_free(music_file_info->year_header);
11505
11506     checked_free(music_file_info->title);
11507     checked_free(music_file_info->artist);
11508     checked_free(music_file_info->album);
11509     checked_free(music_file_info->year);
11510
11511     free(music_file_info);
11512
11513     music_file_info = next;
11514   }
11515
11516   new = &music_file_info;
11517
11518   for (i = 0; i < num_music; i++)
11519   {
11520     music = getMusicListEntry(i);
11521
11522     if (music->filename == NULL)
11523       continue;
11524
11525     if (strEqual(music->filename, UNDEFINED_FILENAME))
11526       continue;
11527
11528     // a configured file may be not recognized as music
11529     if (!FileIsMusic(music->filename))
11530       continue;
11531
11532     if (!music_info_listed(music_file_info, music->filename))
11533     {
11534       *new = get_music_file_info(music->filename, i);
11535
11536       if (*new != NULL)
11537         new = &(*new)->next;
11538     }
11539   }
11540
11541   if ((dir = openDirectory(music_directory)) == NULL)
11542   {
11543     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
11544     return;
11545   }
11546
11547   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
11548   {
11549     char *basename = dir_entry->basename;
11550     boolean music_already_used = FALSE;
11551     int i;
11552
11553     // skip all music files that are configured in music config file
11554     for (i = 0; i < num_music; i++)
11555     {
11556       music = getMusicListEntry(i);
11557
11558       if (music->filename == NULL)
11559         continue;
11560
11561       if (strEqual(basename, music->filename))
11562       {
11563         music_already_used = TRUE;
11564         break;
11565       }
11566     }
11567
11568     if (music_already_used)
11569       continue;
11570
11571     if (!FileIsMusic(dir_entry->filename))
11572       continue;
11573
11574     if (!music_info_listed(music_file_info, basename))
11575     {
11576       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11577
11578       if (*new != NULL)
11579         new = &(*new)->next;
11580     }
11581
11582     num_music_noconf++;
11583   }
11584
11585   closeDirectory(dir);
11586
11587   for (i = 0; i < num_sounds; i++)
11588   {
11589     sound = getSoundListEntry(i);
11590
11591     if (sound->filename == NULL)
11592       continue;
11593
11594     if (strEqual(sound->filename, UNDEFINED_FILENAME))
11595       continue;
11596
11597     // a configured file may be not recognized as sound
11598     if (!FileIsSound(sound->filename))
11599       continue;
11600
11601     if (!sound_info_listed(music_file_info, sound->filename))
11602     {
11603       *new = get_sound_file_info(sound->filename, i);
11604       if (*new != NULL)
11605         new = &(*new)->next;
11606     }
11607   }
11608 }
11609
11610 static void add_helpanim_entry(int element, int action, int direction,
11611                                int delay, int *num_list_entries)
11612 {
11613   struct HelpAnimInfo *new_list_entry;
11614   (*num_list_entries)++;
11615
11616   helpanim_info =
11617     checked_realloc(helpanim_info,
11618                     *num_list_entries * sizeof(struct HelpAnimInfo));
11619   new_list_entry = &helpanim_info[*num_list_entries - 1];
11620
11621   new_list_entry->element = element;
11622   new_list_entry->action = action;
11623   new_list_entry->direction = direction;
11624   new_list_entry->delay = delay;
11625 }
11626
11627 static void print_unknown_token(char *filename, char *token, int token_nr)
11628 {
11629   if (token_nr == 0)
11630   {
11631     Error(ERR_INFO_LINE, "-");
11632     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11633     Error(ERR_INFO, "- config file: '%s'", filename);
11634   }
11635
11636   Error(ERR_INFO, "- token: '%s'", token);
11637 }
11638
11639 static void print_unknown_token_end(int token_nr)
11640 {
11641   if (token_nr > 0)
11642     Error(ERR_INFO_LINE, "-");
11643 }
11644
11645 void LoadHelpAnimInfo(void)
11646 {
11647   char *filename = getHelpAnimFilename();
11648   SetupFileList *setup_file_list = NULL, *list;
11649   SetupFileHash *element_hash, *action_hash, *direction_hash;
11650   int num_list_entries = 0;
11651   int num_unknown_tokens = 0;
11652   int i;
11653
11654   if (fileExists(filename))
11655     setup_file_list = loadSetupFileList(filename);
11656
11657   if (setup_file_list == NULL)
11658   {
11659     // use reliable default values from static configuration
11660     SetupFileList *insert_ptr;
11661
11662     insert_ptr = setup_file_list =
11663       newSetupFileList(helpanim_config[0].token,
11664                        helpanim_config[0].value);
11665
11666     for (i = 1; helpanim_config[i].token; i++)
11667       insert_ptr = addListEntry(insert_ptr,
11668                                 helpanim_config[i].token,
11669                                 helpanim_config[i].value);
11670   }
11671
11672   element_hash   = newSetupFileHash();
11673   action_hash    = newSetupFileHash();
11674   direction_hash = newSetupFileHash();
11675
11676   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11677     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11678
11679   for (i = 0; i < NUM_ACTIONS; i++)
11680     setHashEntry(action_hash, element_action_info[i].suffix,
11681                  i_to_a(element_action_info[i].value));
11682
11683   // do not store direction index (bit) here, but direction value!
11684   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11685     setHashEntry(direction_hash, element_direction_info[i].suffix,
11686                  i_to_a(1 << element_direction_info[i].value));
11687
11688   for (list = setup_file_list; list != NULL; list = list->next)
11689   {
11690     char *element_token, *action_token, *direction_token;
11691     char *element_value, *action_value, *direction_value;
11692     int delay = atoi(list->value);
11693
11694     if (strEqual(list->token, "end"))
11695     {
11696       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11697
11698       continue;
11699     }
11700
11701     /* first try to break element into element/action/direction parts;
11702        if this does not work, also accept combined "element[.act][.dir]"
11703        elements (like "dynamite.active"), which are unique elements */
11704
11705     if (strchr(list->token, '.') == NULL)       // token contains no '.'
11706     {
11707       element_value = getHashEntry(element_hash, list->token);
11708       if (element_value != NULL)        // element found
11709         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11710                            &num_list_entries);
11711       else
11712       {
11713         // no further suffixes found -- this is not an element
11714         print_unknown_token(filename, list->token, num_unknown_tokens++);
11715       }
11716
11717       continue;
11718     }
11719
11720     // token has format "<prefix>.<something>"
11721
11722     action_token = strchr(list->token, '.');    // suffix may be action ...
11723     direction_token = action_token;             // ... or direction
11724
11725     element_token = getStringCopy(list->token);
11726     *strchr(element_token, '.') = '\0';
11727
11728     element_value = getHashEntry(element_hash, element_token);
11729
11730     if (element_value == NULL)          // this is no element
11731     {
11732       element_value = getHashEntry(element_hash, list->token);
11733       if (element_value != NULL)        // combined element found
11734         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11735                            &num_list_entries);
11736       else
11737         print_unknown_token(filename, list->token, num_unknown_tokens++);
11738
11739       free(element_token);
11740
11741       continue;
11742     }
11743
11744     action_value = getHashEntry(action_hash, action_token);
11745
11746     if (action_value != NULL)           // action found
11747     {
11748       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11749                     &num_list_entries);
11750
11751       free(element_token);
11752
11753       continue;
11754     }
11755
11756     direction_value = getHashEntry(direction_hash, direction_token);
11757
11758     if (direction_value != NULL)        // direction found
11759     {
11760       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11761                          &num_list_entries);
11762
11763       free(element_token);
11764
11765       continue;
11766     }
11767
11768     if (strchr(action_token + 1, '.') == NULL)
11769     {
11770       // no further suffixes found -- this is not an action nor direction
11771
11772       element_value = getHashEntry(element_hash, list->token);
11773       if (element_value != NULL)        // combined element found
11774         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11775                            &num_list_entries);
11776       else
11777         print_unknown_token(filename, list->token, num_unknown_tokens++);
11778
11779       free(element_token);
11780
11781       continue;
11782     }
11783
11784     // token has format "<prefix>.<suffix>.<something>"
11785
11786     direction_token = strchr(action_token + 1, '.');
11787
11788     action_token = getStringCopy(action_token);
11789     *strchr(action_token + 1, '.') = '\0';
11790
11791     action_value = getHashEntry(action_hash, action_token);
11792
11793     if (action_value == NULL)           // this is no action
11794     {
11795       element_value = getHashEntry(element_hash, list->token);
11796       if (element_value != NULL)        // combined element found
11797         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11798                            &num_list_entries);
11799       else
11800         print_unknown_token(filename, list->token, num_unknown_tokens++);
11801
11802       free(element_token);
11803       free(action_token);
11804
11805       continue;
11806     }
11807
11808     direction_value = getHashEntry(direction_hash, direction_token);
11809
11810     if (direction_value != NULL)        // direction found
11811     {
11812       add_helpanim_entry(atoi(element_value), atoi(action_value),
11813                          atoi(direction_value), delay, &num_list_entries);
11814
11815       free(element_token);
11816       free(action_token);
11817
11818       continue;
11819     }
11820
11821     // this is no direction
11822
11823     element_value = getHashEntry(element_hash, list->token);
11824     if (element_value != NULL)          // combined element found
11825       add_helpanim_entry(atoi(element_value), -1, -1, delay,
11826                          &num_list_entries);
11827     else
11828       print_unknown_token(filename, list->token, num_unknown_tokens++);
11829
11830     free(element_token);
11831     free(action_token);
11832   }
11833
11834   print_unknown_token_end(num_unknown_tokens);
11835
11836   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11837   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
11838
11839   freeSetupFileList(setup_file_list);
11840   freeSetupFileHash(element_hash);
11841   freeSetupFileHash(action_hash);
11842   freeSetupFileHash(direction_hash);
11843
11844 #if 0
11845   for (i = 0; i < num_list_entries; i++)
11846     printf("::: '%s': %d, %d, %d => %d\n",
11847            EL_NAME(helpanim_info[i].element),
11848            helpanim_info[i].element,
11849            helpanim_info[i].action,
11850            helpanim_info[i].direction,
11851            helpanim_info[i].delay);
11852 #endif
11853 }
11854
11855 void LoadHelpTextInfo(void)
11856 {
11857   char *filename = getHelpTextFilename();
11858   int i;
11859
11860   if (helptext_info != NULL)
11861   {
11862     freeSetupFileHash(helptext_info);
11863     helptext_info = NULL;
11864   }
11865
11866   if (fileExists(filename))
11867     helptext_info = loadSetupFileHash(filename);
11868
11869   if (helptext_info == NULL)
11870   {
11871     // use reliable default values from static configuration
11872     helptext_info = newSetupFileHash();
11873
11874     for (i = 0; helptext_config[i].token; i++)
11875       setHashEntry(helptext_info,
11876                    helptext_config[i].token,
11877                    helptext_config[i].value);
11878   }
11879
11880 #if 0
11881   BEGIN_HASH_ITERATION(helptext_info, itr)
11882   {
11883     printf("::: '%s' => '%s'\n",
11884            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
11885   }
11886   END_HASH_ITERATION(hash, itr)
11887 #endif
11888 }
11889
11890
11891 // ----------------------------------------------------------------------------
11892 // convert levels
11893 // ----------------------------------------------------------------------------
11894
11895 #define MAX_NUM_CONVERT_LEVELS          1000
11896
11897 void ConvertLevels(void)
11898 {
11899   static LevelDirTree *convert_leveldir = NULL;
11900   static int convert_level_nr = -1;
11901   static int num_levels_handled = 0;
11902   static int num_levels_converted = 0;
11903   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
11904   int i;
11905
11906   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
11907                                                global.convert_leveldir);
11908
11909   if (convert_leveldir == NULL)
11910     Error(ERR_EXIT, "no such level identifier: '%s'",
11911           global.convert_leveldir);
11912
11913   leveldir_current = convert_leveldir;
11914
11915   if (global.convert_level_nr != -1)
11916   {
11917     convert_leveldir->first_level = global.convert_level_nr;
11918     convert_leveldir->last_level  = global.convert_level_nr;
11919   }
11920
11921   convert_level_nr = convert_leveldir->first_level;
11922
11923   PrintLine("=", 79);
11924   Print("Converting levels\n");
11925   PrintLine("-", 79);
11926   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
11927   Print("Level series name:       '%s'\n", convert_leveldir->name);
11928   Print("Level series author:     '%s'\n", convert_leveldir->author);
11929   Print("Number of levels:        %d\n",   convert_leveldir->levels);
11930   PrintLine("=", 79);
11931   Print("\n");
11932
11933   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11934     levels_failed[i] = FALSE;
11935
11936   while (convert_level_nr <= convert_leveldir->last_level)
11937   {
11938     char *level_filename;
11939     boolean new_level;
11940
11941     level_nr = convert_level_nr++;
11942
11943     Print("Level %03d: ", level_nr);
11944
11945     LoadLevel(level_nr);
11946     if (level.no_level_file || level.no_valid_file)
11947     {
11948       Print("(no level)\n");
11949       continue;
11950     }
11951
11952     Print("converting level ... ");
11953
11954     level_filename = getDefaultLevelFilename(level_nr);
11955     new_level = !fileExists(level_filename);
11956
11957     if (new_level)
11958     {
11959       SaveLevel(level_nr);
11960
11961       num_levels_converted++;
11962
11963       Print("converted.\n");
11964     }
11965     else
11966     {
11967       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
11968         levels_failed[level_nr] = TRUE;
11969
11970       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
11971     }
11972
11973     num_levels_handled++;
11974   }
11975
11976   Print("\n");
11977   PrintLine("=", 79);
11978   Print("Number of levels handled: %d\n", num_levels_handled);
11979   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
11980          (num_levels_handled ?
11981           num_levels_converted * 100 / num_levels_handled : 0));
11982   PrintLine("-", 79);
11983   Print("Summary (for automatic parsing by scripts):\n");
11984   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
11985          convert_leveldir->identifier, num_levels_converted,
11986          num_levels_handled,
11987          (num_levels_handled ?
11988           num_levels_converted * 100 / num_levels_handled : 0));
11989
11990   if (num_levels_handled != num_levels_converted)
11991   {
11992     Print(", FAILED:");
11993     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11994       if (levels_failed[i])
11995         Print(" %03d", i);
11996   }
11997
11998   Print("\n");
11999   PrintLine("=", 79);
12000
12001   CloseAllAndExit(0);
12002 }
12003
12004
12005 // ----------------------------------------------------------------------------
12006 // create and save images for use in level sketches (raw BMP format)
12007 // ----------------------------------------------------------------------------
12008
12009 void CreateLevelSketchImages(void)
12010 {
12011   Bitmap *bitmap1;
12012   Bitmap *bitmap2;
12013   int i;
12014
12015   InitElementPropertiesGfxElement();
12016
12017   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12018   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12019
12020   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12021   {
12022     int element = getMappedElement(i);
12023     char basename1[16];
12024     char basename2[16];
12025     char *filename1;
12026     char *filename2;
12027
12028     sprintf(basename1, "%04d.bmp", i);
12029     sprintf(basename2, "%04ds.bmp", i);
12030
12031     filename1 = getPath2(global.create_images_dir, basename1);
12032     filename2 = getPath2(global.create_images_dir, basename2);
12033
12034     DrawSizedElement(0, 0, element, TILESIZE);
12035     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12036
12037     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12038       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
12039
12040     DrawSizedElement(0, 0, element, MINI_TILESIZE);
12041     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12042
12043     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12044       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
12045
12046     free(filename1);
12047     free(filename2);
12048
12049     // create corresponding SQL statements (for normal and small images)
12050     if (i < 1000)
12051     {
12052       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12053       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12054     }
12055
12056     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12057     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12058
12059     // optional: create content for forum level sketch demonstration post
12060     if (options.debug)
12061       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12062   }
12063
12064   FreeBitmap(bitmap1);
12065   FreeBitmap(bitmap2);
12066
12067   if (options.debug)
12068     fprintf(stderr, "\n");
12069
12070   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
12071
12072   CloseAllAndExit(0);
12073 }
12074
12075
12076 // ----------------------------------------------------------------------------
12077 // create and save images for custom and group elements (raw BMP format)
12078 // ----------------------------------------------------------------------------
12079
12080 void CreateCustomElementImages(char *directory)
12081 {
12082   char *src_basename = "RocksCE-template.ilbm";
12083   char *dst_basename = "RocksCE.bmp";
12084   char *src_filename = getPath2(directory, src_basename);
12085   char *dst_filename = getPath2(directory, dst_basename);
12086   Bitmap *src_bitmap;
12087   Bitmap *bitmap;
12088   int yoffset_ce = 0;
12089   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12090   int i;
12091
12092   InitVideoDefaults();
12093
12094   ReCreateBitmap(&backbuffer, video.width, video.height);
12095
12096   src_bitmap = LoadImage(src_filename);
12097
12098   bitmap = CreateBitmap(TILEX * 16 * 2,
12099                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12100                         DEFAULT_DEPTH);
12101
12102   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12103   {
12104     int x = i % 16;
12105     int y = i / 16;
12106     int ii = i + 1;
12107     int j;
12108
12109     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12110                TILEX * x, TILEY * y + yoffset_ce);
12111
12112     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12113                TILEX, TILEY,
12114                TILEX * x + TILEX * 16,
12115                TILEY * y + yoffset_ce);
12116
12117     for (j = 2; j >= 0; j--)
12118     {
12119       int c = ii % 10;
12120
12121       BlitBitmap(src_bitmap, bitmap,
12122                  TILEX + c * 7, 0, 6, 10,
12123                  TILEX * x + 6 + j * 7,
12124                  TILEY * y + 11 + yoffset_ce);
12125
12126       BlitBitmap(src_bitmap, bitmap,
12127                  TILEX + c * 8, TILEY, 6, 10,
12128                  TILEX * 16 + TILEX * x + 6 + j * 8,
12129                  TILEY * y + 10 + yoffset_ce);
12130
12131       ii /= 10;
12132     }
12133   }
12134
12135   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12136   {
12137     int x = i % 16;
12138     int y = i / 16;
12139     int ii = i + 1;
12140     int j;
12141
12142     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12143                TILEX * x, TILEY * y + yoffset_ge);
12144
12145     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12146                TILEX, TILEY,
12147                TILEX * x + TILEX * 16,
12148                TILEY * y + yoffset_ge);
12149
12150     for (j = 1; j >= 0; j--)
12151     {
12152       int c = ii % 10;
12153
12154       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12155                  TILEX * x + 6 + j * 10,
12156                  TILEY * y + 11 + yoffset_ge);
12157
12158       BlitBitmap(src_bitmap, bitmap,
12159                  TILEX + c * 8, TILEY + 12, 6, 10,
12160                  TILEX * 16 + TILEX * x + 10 + j * 8,
12161                  TILEY * y + 10 + yoffset_ge);
12162
12163       ii /= 10;
12164     }
12165   }
12166
12167   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12168     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
12169
12170   FreeBitmap(bitmap);
12171
12172   CloseAllAndExit(0);
12173 }