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