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