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