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