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