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