2f249ada3eba63aeb204a04cd1def50325c8295f
[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 LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7623 {
7624   tape->file_version = getFileVersion(file);
7625   tape->game_version = getFileVersion(file);
7626
7627   return chunk_size;
7628 }
7629
7630 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7631 {
7632   int i;
7633
7634   tape->random_seed = getFile32BitBE(file);
7635   tape->date        = getFile32BitBE(file);
7636   tape->length      = getFile32BitBE(file);
7637
7638   // read header fields that are new since version 1.2
7639   if (tape->file_version >= FILE_VERSION_1_2)
7640   {
7641     byte store_participating_players = getFile8Bit(file);
7642     int engine_version;
7643
7644     // since version 1.2, tapes store which players participate in the tape
7645     tape->num_participating_players = 0;
7646     for (i = 0; i < MAX_PLAYERS; i++)
7647     {
7648       tape->player_participates[i] = FALSE;
7649
7650       if (store_participating_players & (1 << i))
7651       {
7652         tape->player_participates[i] = TRUE;
7653         tape->num_participating_players++;
7654       }
7655     }
7656
7657     tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7658
7659     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7660
7661     engine_version = getFileVersion(file);
7662     if (engine_version > 0)
7663       tape->engine_version = engine_version;
7664     else
7665       tape->engine_version = tape->game_version;
7666   }
7667
7668   return chunk_size;
7669 }
7670
7671 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7672 {
7673   int level_identifier_size;
7674   int i;
7675
7676   level_identifier_size = getFile16BitBE(file);
7677
7678   tape->level_identifier =
7679     checked_realloc(tape->level_identifier, level_identifier_size);
7680
7681   for (i = 0; i < level_identifier_size; i++)
7682     tape->level_identifier[i] = getFile8Bit(file);
7683
7684   tape->level_nr = getFile16BitBE(file);
7685
7686   chunk_size = 2 + level_identifier_size + 2;
7687
7688   return chunk_size;
7689 }
7690
7691 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7692 {
7693   int i, j;
7694   int tape_pos_size =
7695     (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7696   int chunk_size_expected = tape_pos_size * tape->length;
7697
7698   if (chunk_size_expected != chunk_size)
7699   {
7700     ReadUnusedBytesFromFile(file, chunk_size);
7701     return chunk_size_expected;
7702   }
7703
7704   for (i = 0; i < tape->length; i++)
7705   {
7706     if (i >= MAX_TAPE_LEN)
7707     {
7708       Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7709             MAX_TAPE_LEN);
7710
7711       // tape too large; read and ignore remaining tape data from this chunk
7712       for (;i < tape->length; i++)
7713         ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7714
7715       break;
7716     }
7717
7718     if (tape->use_mouse)
7719     {
7720       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
7721       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
7722       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7723
7724       tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7725     }
7726     else
7727     {
7728       for (j = 0; j < MAX_PLAYERS; j++)
7729       {
7730         tape->pos[i].action[j] = MV_NONE;
7731
7732         if (tape->player_participates[j])
7733           tape->pos[i].action[j] = getFile8Bit(file);
7734       }
7735     }
7736
7737     tape->pos[i].delay = getFile8Bit(file);
7738
7739     if (tape->file_version == FILE_VERSION_1_0)
7740     {
7741       // eliminate possible diagonal moves in old tapes
7742       // this is only for backward compatibility
7743
7744       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7745       byte action = tape->pos[i].action[0];
7746       int k, num_moves = 0;
7747
7748       for (k = 0; k<4; k++)
7749       {
7750         if (action & joy_dir[k])
7751         {
7752           tape->pos[i + num_moves].action[0] = joy_dir[k];
7753           if (num_moves > 0)
7754             tape->pos[i + num_moves].delay = 0;
7755           num_moves++;
7756         }
7757       }
7758
7759       if (num_moves > 1)
7760       {
7761         num_moves--;
7762         i += num_moves;
7763         tape->length += num_moves;
7764       }
7765     }
7766     else if (tape->file_version < FILE_VERSION_2_0)
7767     {
7768       // convert pre-2.0 tapes to new tape format
7769
7770       if (tape->pos[i].delay > 1)
7771       {
7772         // action part
7773         tape->pos[i + 1] = tape->pos[i];
7774         tape->pos[i + 1].delay = 1;
7775
7776         // delay part
7777         for (j = 0; j < MAX_PLAYERS; j++)
7778           tape->pos[i].action[j] = MV_NONE;
7779         tape->pos[i].delay--;
7780
7781         i++;
7782         tape->length++;
7783       }
7784     }
7785
7786     if (checkEndOfFile(file))
7787       break;
7788   }
7789
7790   if (i != tape->length)
7791     chunk_size = tape_pos_size * i;
7792
7793   return chunk_size;
7794 }
7795
7796 static void LoadTape_SokobanSolution(char *filename)
7797 {
7798   File *file;
7799   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7800
7801   if (!(file = openFile(filename, MODE_READ)))
7802   {
7803     tape.no_valid_file = TRUE;
7804
7805     return;
7806   }
7807
7808   while (!checkEndOfFile(file))
7809   {
7810     unsigned char c = getByteFromFile(file);
7811
7812     if (checkEndOfFile(file))
7813       break;
7814
7815     switch (c)
7816     {
7817       case 'u':
7818       case 'U':
7819         tape.pos[tape.length].action[0] = MV_UP;
7820         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7821         tape.length++;
7822         break;
7823
7824       case 'd':
7825       case 'D':
7826         tape.pos[tape.length].action[0] = MV_DOWN;
7827         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7828         tape.length++;
7829         break;
7830
7831       case 'l':
7832       case 'L':
7833         tape.pos[tape.length].action[0] = MV_LEFT;
7834         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7835         tape.length++;
7836         break;
7837
7838       case 'r':
7839       case 'R':
7840         tape.pos[tape.length].action[0] = MV_RIGHT;
7841         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7842         tape.length++;
7843         break;
7844
7845       case '\n':
7846       case '\r':
7847       case '\t':
7848       case ' ':
7849         // ignore white-space characters
7850         break;
7851
7852       default:
7853         tape.no_valid_file = TRUE;
7854
7855         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7856
7857         break;
7858     }
7859   }
7860
7861   closeFile(file);
7862
7863   if (tape.no_valid_file)
7864     return;
7865
7866   tape.length_frames  = GetTapeLengthFrames();
7867   tape.length_seconds = GetTapeLengthSeconds();
7868 }
7869
7870 void LoadTapeFromFilename(char *filename)
7871 {
7872   char cookie[MAX_LINE_LEN];
7873   char chunk_name[CHUNK_ID_LEN + 1];
7874   File *file;
7875   int chunk_size;
7876
7877   // always start with reliable default values
7878   setTapeInfoToDefaults();
7879
7880   if (strSuffix(filename, ".sln"))
7881   {
7882     LoadTape_SokobanSolution(filename);
7883
7884     return;
7885   }
7886
7887   if (!(file = openFile(filename, MODE_READ)))
7888   {
7889     tape.no_valid_file = TRUE;
7890
7891     return;
7892   }
7893
7894   getFileChunkBE(file, chunk_name, NULL);
7895   if (strEqual(chunk_name, "RND1"))
7896   {
7897     getFile32BitBE(file);               // not used
7898
7899     getFileChunkBE(file, chunk_name, NULL);
7900     if (!strEqual(chunk_name, "TAPE"))
7901     {
7902       tape.no_valid_file = TRUE;
7903
7904       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7905
7906       closeFile(file);
7907
7908       return;
7909     }
7910   }
7911   else  // check for pre-2.0 file format with cookie string
7912   {
7913     strcpy(cookie, chunk_name);
7914     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7915       cookie[4] = '\0';
7916     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7917       cookie[strlen(cookie) - 1] = '\0';
7918
7919     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7920     {
7921       tape.no_valid_file = TRUE;
7922
7923       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7924
7925       closeFile(file);
7926
7927       return;
7928     }
7929
7930     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7931     {
7932       tape.no_valid_file = TRUE;
7933
7934       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7935
7936       closeFile(file);
7937
7938       return;
7939     }
7940
7941     // pre-2.0 tape files have no game version, so use file version here
7942     tape.game_version = tape.file_version;
7943   }
7944
7945   if (tape.file_version < FILE_VERSION_1_2)
7946   {
7947     // tape files from versions before 1.2.0 without chunk structure
7948     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7949     LoadTape_BODY(file, 2 * tape.length,      &tape);
7950   }
7951   else
7952   {
7953     static struct
7954     {
7955       char *name;
7956       int size;
7957       int (*loader)(File *, int, struct TapeInfo *);
7958     }
7959     chunk_info[] =
7960     {
7961       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
7962       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
7963       { "INFO", -1,                     LoadTape_INFO },
7964       { "BODY", -1,                     LoadTape_BODY },
7965       {  NULL,  0,                      NULL }
7966     };
7967
7968     while (getFileChunkBE(file, chunk_name, &chunk_size))
7969     {
7970       int i = 0;
7971
7972       while (chunk_info[i].name != NULL &&
7973              !strEqual(chunk_name, chunk_info[i].name))
7974         i++;
7975
7976       if (chunk_info[i].name == NULL)
7977       {
7978         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7979               chunk_name, filename);
7980         ReadUnusedBytesFromFile(file, chunk_size);
7981       }
7982       else if (chunk_info[i].size != -1 &&
7983                chunk_info[i].size != chunk_size)
7984       {
7985         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7986               chunk_size, chunk_name, filename);
7987         ReadUnusedBytesFromFile(file, chunk_size);
7988       }
7989       else
7990       {
7991         // call function to load this tape chunk
7992         int chunk_size_expected =
7993           (chunk_info[i].loader)(file, chunk_size, &tape);
7994
7995         // the size of some chunks cannot be checked before reading other
7996         // chunks first (like "HEAD" and "BODY") that contain some header
7997         // information, so check them here
7998         if (chunk_size_expected != chunk_size)
7999         {
8000           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8001                 chunk_size, chunk_name, filename);
8002         }
8003       }
8004     }
8005   }
8006
8007   closeFile(file);
8008
8009   tape.length_frames  = GetTapeLengthFrames();
8010   tape.length_seconds = GetTapeLengthSeconds();
8011
8012 #if 0
8013   printf("::: tape file version: %d\n",   tape.file_version);
8014   printf("::: tape game version: %d\n",   tape.game_version);
8015   printf("::: tape engine version: %d\n", tape.engine_version);
8016 #endif
8017 }
8018
8019 void LoadTape(int nr)
8020 {
8021   char *filename = getTapeFilename(nr);
8022
8023   LoadTapeFromFilename(filename);
8024 }
8025
8026 void LoadSolutionTape(int nr)
8027 {
8028   char *filename = getSolutionTapeFilename(nr);
8029
8030   LoadTapeFromFilename(filename);
8031
8032   if (TAPE_IS_EMPTY(tape) &&
8033       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8034       level.native_sp_level->demo.is_available)
8035     CopyNativeTape_SP_to_RND(&level);
8036 }
8037
8038 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8039 {
8040   putFileVersion(file, tape->file_version);
8041   putFileVersion(file, tape->game_version);
8042 }
8043
8044 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8045 {
8046   int i;
8047   byte store_participating_players = 0;
8048
8049   // set bits for participating players for compact storage
8050   for (i = 0; i < MAX_PLAYERS; i++)
8051     if (tape->player_participates[i])
8052       store_participating_players |= (1 << i);
8053
8054   putFile32BitBE(file, tape->random_seed);
8055   putFile32BitBE(file, tape->date);
8056   putFile32BitBE(file, tape->length);
8057
8058   putFile8Bit(file, store_participating_players);
8059
8060   putFile8Bit(file, (tape->use_mouse ? 1 : 0));
8061
8062   // unused bytes not at the end here for 4-byte alignment of engine_version
8063   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8064
8065   putFileVersion(file, tape->engine_version);
8066 }
8067
8068 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8069 {
8070   int level_identifier_size = strlen(tape->level_identifier) + 1;
8071   int i;
8072
8073   putFile16BitBE(file, level_identifier_size);
8074
8075   for (i = 0; i < level_identifier_size; i++)
8076     putFile8Bit(file, tape->level_identifier[i]);
8077
8078   putFile16BitBE(file, tape->level_nr);
8079 }
8080
8081 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8082 {
8083   int i, j;
8084
8085   for (i = 0; i < tape->length; i++)
8086   {
8087     if (tape->use_mouse)
8088     {
8089       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8090       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8091       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8092     }
8093     else
8094     {
8095       for (j = 0; j < MAX_PLAYERS; j++)
8096         if (tape->player_participates[j])
8097           putFile8Bit(file, tape->pos[i].action[j]);
8098     }
8099
8100     putFile8Bit(file, tape->pos[i].delay);
8101   }
8102 }
8103
8104 void SaveTape(int nr)
8105 {
8106   char *filename = getTapeFilename(nr);
8107   FILE *file;
8108   int tape_pos_size;
8109   int info_chunk_size;
8110   int body_chunk_size;
8111   int i;
8112
8113   InitTapeDirectory(leveldir_current->subdir);
8114
8115   if (!(file = fopen(filename, MODE_WRITE)))
8116   {
8117     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8118     return;
8119   }
8120
8121   tape.file_version = FILE_VERSION_ACTUAL;
8122   tape.game_version = GAME_VERSION_ACTUAL;
8123
8124   tape.num_participating_players = 0;
8125
8126   // count number of participating players
8127   for (i = 0; i < MAX_PLAYERS; i++)
8128     if (tape.player_participates[i])
8129       tape.num_participating_players++;
8130
8131   tape_pos_size = (tape.use_mouse ? 3 : tape.num_participating_players) + 1;
8132
8133   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8134   body_chunk_size = tape_pos_size * tape.length;
8135
8136   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8137   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8138
8139   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8140   SaveTape_VERS(file, &tape);
8141
8142   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8143   SaveTape_HEAD(file, &tape);
8144
8145   putFileChunkBE(file, "INFO", info_chunk_size);
8146   SaveTape_INFO(file, &tape);
8147
8148   putFileChunkBE(file, "BODY", body_chunk_size);
8149   SaveTape_BODY(file, &tape);
8150
8151   fclose(file);
8152
8153   SetFilePermissions(filename, PERMS_PRIVATE);
8154
8155   tape.changed = FALSE;
8156 }
8157
8158 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8159                                   unsigned int req_state_added)
8160 {
8161   char *filename = getTapeFilename(nr);
8162   boolean new_tape = !fileExists(filename);
8163   boolean tape_saved = FALSE;
8164
8165   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8166   {
8167     SaveTape(nr);
8168
8169     if (new_tape)
8170       Request(msg_saved, REQ_CONFIRM | req_state_added);
8171
8172     tape_saved = TRUE;
8173   }
8174
8175   return tape_saved;
8176 }
8177
8178 boolean SaveTapeChecked(int nr)
8179 {
8180   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8181 }
8182
8183 boolean SaveTapeChecked_LevelSolved(int nr)
8184 {
8185   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8186                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
8187 }
8188
8189 void DumpTape(struct TapeInfo *tape)
8190 {
8191   int tape_frame_counter;
8192   int i, j;
8193
8194   if (tape->no_valid_file)
8195   {
8196     Error(ERR_WARN, "cannot dump -- no valid tape file found");
8197
8198     return;
8199   }
8200
8201   PrintLine("-", 79);
8202   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8203         tape->level_nr, tape->file_version, tape->game_version);
8204   Print("                  (effective engine version %08d)\n",
8205         tape->engine_version);
8206   Print("Level series identifier: '%s'\n", tape->level_identifier);
8207   PrintLine("-", 79);
8208
8209   tape_frame_counter = 0;
8210
8211   for (i = 0; i < tape->length; i++)
8212   {
8213     if (i >= MAX_TAPE_LEN)
8214       break;
8215
8216     Print("%04d: ", i);
8217
8218     for (j = 0; j < MAX_PLAYERS; j++)
8219     {
8220       if (tape->player_participates[j])
8221       {
8222         int action = tape->pos[i].action[j];
8223
8224         Print("%d:%02x ", j, action);
8225         Print("[%c%c%c%c|%c%c] - ",
8226               (action & JOY_LEFT ? '<' : ' '),
8227               (action & JOY_RIGHT ? '>' : ' '),
8228               (action & JOY_UP ? '^' : ' '),
8229               (action & JOY_DOWN ? 'v' : ' '),
8230               (action & JOY_BUTTON_1 ? '1' : ' '),
8231               (action & JOY_BUTTON_2 ? '2' : ' '));
8232       }
8233     }
8234
8235     Print("(%03d) ", tape->pos[i].delay);
8236     Print("[%05d]\n", tape_frame_counter);
8237
8238     tape_frame_counter += tape->pos[i].delay;
8239   }
8240
8241   PrintLine("-", 79);
8242 }
8243
8244
8245 // ============================================================================
8246 // score file functions
8247 // ============================================================================
8248
8249 void LoadScore(int nr)
8250 {
8251   int i;
8252   char *filename = getScoreFilename(nr);
8253   char cookie[MAX_LINE_LEN];
8254   char line[MAX_LINE_LEN];
8255   char *line_ptr;
8256   FILE *file;
8257
8258   // always start with reliable default values
8259   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8260   {
8261     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8262     highscore[i].Score = 0;
8263   }
8264
8265   if (!(file = fopen(filename, MODE_READ)))
8266     return;
8267
8268   // check file identifier
8269   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8270     cookie[0] = '\0';
8271   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8272     cookie[strlen(cookie) - 1] = '\0';
8273
8274   if (!checkCookieString(cookie, SCORE_COOKIE))
8275   {
8276     Error(ERR_WARN, "unknown format of score file '%s'", filename);
8277     fclose(file);
8278     return;
8279   }
8280
8281   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8282   {
8283     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8284       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8285     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8286       line[0] = '\0';
8287
8288     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8289       line[strlen(line) - 1] = '\0';
8290
8291     for (line_ptr = line; *line_ptr; line_ptr++)
8292     {
8293       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8294       {
8295         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8296         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8297         break;
8298       }
8299     }
8300   }
8301
8302   fclose(file);
8303 }
8304
8305 void SaveScore(int nr)
8306 {
8307   int i;
8308   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8309   char *filename = getScoreFilename(nr);
8310   FILE *file;
8311
8312   // used instead of "leveldir_current->subdir" (for network games)
8313   InitScoreDirectory(levelset.identifier);
8314
8315   if (!(file = fopen(filename, MODE_WRITE)))
8316   {
8317     Error(ERR_WARN, "cannot save score for level %d", nr);
8318     return;
8319   }
8320
8321   fprintf(file, "%s\n\n", SCORE_COOKIE);
8322
8323   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8324     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8325
8326   fclose(file);
8327
8328   SetFilePermissions(filename, permissions);
8329 }
8330
8331
8332 // ============================================================================
8333 // setup file functions
8334 // ============================================================================
8335
8336 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8337
8338
8339 static struct TokenInfo global_setup_tokens[] =
8340 {
8341   {
8342     TYPE_STRING,
8343     &setup.player_name,                         "player_name"
8344   },
8345   {
8346     TYPE_SWITCH,
8347     &setup.sound,                               "sound"
8348   },
8349   {
8350     TYPE_SWITCH,
8351     &setup.sound_loops,                         "repeating_sound_loops"
8352   },
8353   {
8354     TYPE_SWITCH,
8355     &setup.sound_music,                         "background_music"
8356   },
8357   {
8358     TYPE_SWITCH,
8359     &setup.sound_simple,                        "simple_sound_effects"
8360   },
8361   {
8362     TYPE_SWITCH,
8363     &setup.toons,                               "toons"
8364   },
8365   {
8366     TYPE_SWITCH,
8367     &setup.scroll_delay,                        "scroll_delay"
8368   },
8369   {
8370     TYPE_SWITCH,
8371     &setup.forced_scroll_delay,                 "forced_scroll_delay"
8372   },
8373   {
8374     TYPE_INTEGER,
8375     &setup.scroll_delay_value,                  "scroll_delay_value"
8376   },
8377   {
8378     TYPE_STRING,
8379     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
8380   },
8381   {
8382     TYPE_INTEGER,
8383     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
8384   },
8385   {
8386     TYPE_SWITCH,
8387     &setup.fade_screens,                        "fade_screens"
8388   },
8389   {
8390     TYPE_SWITCH,
8391     &setup.autorecord,                          "automatic_tape_recording"
8392   },
8393   {
8394     TYPE_SWITCH,
8395     &setup.show_titlescreen,                    "show_titlescreen"
8396   },
8397   {
8398     TYPE_SWITCH,
8399     &setup.quick_doors,                         "quick_doors"
8400   },
8401   {
8402     TYPE_SWITCH,
8403     &setup.team_mode,                           "team_mode"
8404   },
8405   {
8406     TYPE_SWITCH,
8407     &setup.handicap,                            "handicap"
8408   },
8409   {
8410     TYPE_SWITCH,
8411     &setup.skip_levels,                         "skip_levels"
8412   },
8413   {
8414     TYPE_SWITCH,
8415     &setup.increment_levels,                    "increment_levels"
8416   },
8417   {
8418     TYPE_SWITCH,
8419     &setup.auto_play_next_level,                "auto_play_next_level"
8420   },
8421   {
8422     TYPE_SWITCH,
8423     &setup.skip_scores_after_game,              "skip_scores_after_game"
8424   },
8425   {
8426     TYPE_SWITCH,
8427     &setup.time_limit,                          "time_limit"
8428   },
8429   {
8430     TYPE_SWITCH,
8431     &setup.fullscreen,                          "fullscreen"
8432   },
8433   {
8434     TYPE_INTEGER,
8435     &setup.window_scaling_percent,              "window_scaling_percent"
8436   },
8437   {
8438     TYPE_STRING,
8439     &setup.window_scaling_quality,              "window_scaling_quality"
8440   },
8441   {
8442     TYPE_STRING,
8443     &setup.screen_rendering_mode,               "screen_rendering_mode"
8444   },
8445   {
8446     TYPE_STRING,
8447     &setup.vsync_mode,                          "vsync_mode"
8448   },
8449   {
8450     TYPE_SWITCH,
8451     &setup.ask_on_escape,                       "ask_on_escape"
8452   },
8453   {
8454     TYPE_SWITCH,
8455     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
8456   },
8457   {
8458     TYPE_SWITCH,
8459     &setup.ask_on_game_over,                    "ask_on_game_over"
8460   },
8461   {
8462     TYPE_SWITCH,
8463     &setup.quick_switch,                        "quick_player_switch"
8464   },
8465   {
8466     TYPE_SWITCH,
8467     &setup.input_on_focus,                      "input_on_focus"
8468   },
8469   {
8470     TYPE_SWITCH,
8471     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
8472   },
8473   {
8474     TYPE_SWITCH,
8475     &setup.game_speed_extended,                 "game_speed_extended"
8476   },
8477   {
8478     TYPE_INTEGER,
8479     &setup.game_frame_delay,                    "game_frame_delay"
8480   },
8481   {
8482     TYPE_SWITCH,
8483     &setup.sp_show_border_elements,             "sp_show_border_elements"
8484   },
8485   {
8486     TYPE_SWITCH,
8487     &setup.small_game_graphics,                 "small_game_graphics"
8488   },
8489   {
8490     TYPE_SWITCH,
8491     &setup.show_snapshot_buttons,               "show_snapshot_buttons"
8492   },
8493   {
8494     TYPE_STRING,
8495     &setup.graphics_set,                        "graphics_set"
8496   },
8497   {
8498     TYPE_STRING,
8499     &setup.sounds_set,                          "sounds_set"
8500   },
8501   {
8502     TYPE_STRING,
8503     &setup.music_set,                           "music_set"
8504   },
8505   {
8506     TYPE_SWITCH3,
8507     &setup.override_level_graphics,             "override_level_graphics"
8508   },
8509   {
8510     TYPE_SWITCH3,
8511     &setup.override_level_sounds,               "override_level_sounds"
8512   },
8513   {
8514     TYPE_SWITCH3,
8515     &setup.override_level_music,                "override_level_music"
8516   },
8517   {
8518     TYPE_INTEGER,
8519     &setup.volume_simple,                       "volume_simple"
8520   },
8521   {
8522     TYPE_INTEGER,
8523     &setup.volume_loops,                        "volume_loops"
8524   },
8525   {
8526     TYPE_INTEGER,
8527     &setup.volume_music,                        "volume_music"
8528   },
8529   {
8530     TYPE_SWITCH,
8531     &setup.network_mode,                        "network_mode"
8532   },
8533   {
8534     TYPE_PLAYER,
8535     &setup.network_player_nr,                   "network_player"
8536   },
8537   {
8538     TYPE_STRING,
8539     &setup.network_server_hostname,             "network_server_hostname"
8540   },
8541   {
8542     TYPE_STRING,
8543     &setup.touch.control_type,                  "touch.control_type"
8544   },
8545   {
8546     TYPE_INTEGER,
8547     &setup.touch.move_distance,                 "touch.move_distance"
8548   },
8549   {
8550     TYPE_INTEGER,
8551     &setup.touch.drop_distance,                 "touch.drop_distance"
8552   },
8553   {
8554     TYPE_INTEGER,
8555     &setup.touch.transparency,                  "touch.transparency"
8556   },
8557   {
8558     TYPE_INTEGER,
8559     &setup.touch.draw_outlined,                 "touch.draw_outlined"
8560   },
8561   {
8562     TYPE_INTEGER,
8563     &setup.touch.draw_pressed,                  "touch.draw_pressed"
8564   },
8565   {
8566     TYPE_INTEGER,
8567     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
8568   },
8569   {
8570     TYPE_INTEGER,
8571     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
8572   },
8573   {
8574     TYPE_INTEGER,
8575     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
8576   },
8577   {
8578     TYPE_INTEGER,
8579     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
8580   },
8581 };
8582
8583 static struct TokenInfo auto_setup_tokens[] =
8584 {
8585   {
8586     TYPE_INTEGER,
8587     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
8588   },
8589 };
8590
8591 static struct TokenInfo editor_setup_tokens[] =
8592 {
8593   {
8594     TYPE_SWITCH,
8595     &setup.editor.el_classic,                   "editor.el_classic"
8596   },
8597   {
8598     TYPE_SWITCH,
8599     &setup.editor.el_custom,                    "editor.el_custom"
8600   },
8601   {
8602     TYPE_SWITCH,
8603     &setup.editor.el_user_defined,              "editor.el_user_defined"
8604   },
8605   {
8606     TYPE_SWITCH,
8607     &setup.editor.el_dynamic,                   "editor.el_dynamic"
8608   },
8609   {
8610     TYPE_SWITCH,
8611     &setup.editor.el_headlines,                 "editor.el_headlines"
8612   },
8613   {
8614     TYPE_SWITCH,
8615     &setup.editor.show_element_token,           "editor.show_element_token"
8616   },
8617 };
8618
8619 static struct TokenInfo editor_cascade_setup_tokens[] =
8620 {
8621   {
8622     TYPE_SWITCH,
8623     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
8624   },
8625   {
8626     TYPE_SWITCH,
8627     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
8628   },
8629   {
8630     TYPE_SWITCH,
8631     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
8632   },
8633   {
8634     TYPE_SWITCH,
8635     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
8636   },
8637   {
8638     TYPE_SWITCH,
8639     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
8640   },
8641   {
8642     TYPE_SWITCH,
8643     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
8644   },
8645   {
8646     TYPE_SWITCH,
8647     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
8648   },
8649   {
8650     TYPE_SWITCH,
8651     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
8652   },
8653   {
8654     TYPE_SWITCH,
8655     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
8656   },
8657   {
8658     TYPE_SWITCH,
8659     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
8660   },
8661   {
8662     TYPE_SWITCH,
8663     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
8664   },
8665   {
8666     TYPE_SWITCH,
8667     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
8668   },
8669   {
8670     TYPE_SWITCH,
8671     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
8672   },
8673   {
8674     TYPE_SWITCH,
8675     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
8676   },
8677   {
8678     TYPE_SWITCH,
8679     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
8680   },
8681   {
8682     TYPE_SWITCH,
8683     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
8684   },
8685   {
8686     TYPE_SWITCH,
8687     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
8688   },
8689 };
8690
8691 static struct TokenInfo shortcut_setup_tokens[] =
8692 {
8693   {
8694     TYPE_KEY_X11,
8695     &setup.shortcut.save_game,                  "shortcut.save_game"
8696   },
8697   {
8698     TYPE_KEY_X11,
8699     &setup.shortcut.load_game,                  "shortcut.load_game"
8700   },
8701   {
8702     TYPE_KEY_X11,
8703     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
8704   },
8705   {
8706     TYPE_KEY_X11,
8707     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
8708   },
8709   {
8710     TYPE_KEY_X11,
8711     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
8712   },
8713   {
8714     TYPE_KEY_X11,
8715     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
8716   },
8717   {
8718     TYPE_KEY_X11,
8719     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
8720   },
8721   {
8722     TYPE_KEY_X11,
8723     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
8724   },
8725   {
8726     TYPE_KEY_X11,
8727     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
8728   },
8729   {
8730     TYPE_KEY_X11,
8731     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
8732   },
8733   {
8734     TYPE_KEY_X11,
8735     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
8736   },
8737   {
8738     TYPE_KEY_X11,
8739     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
8740   },
8741   {
8742     TYPE_KEY_X11,
8743     &setup.shortcut.tape_record,                "shortcut.tape_record"
8744   },
8745   {
8746     TYPE_KEY_X11,
8747     &setup.shortcut.tape_play,                  "shortcut.tape_play"
8748   },
8749   {
8750     TYPE_KEY_X11,
8751     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
8752   },
8753   {
8754     TYPE_KEY_X11,
8755     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
8756   },
8757   {
8758     TYPE_KEY_X11,
8759     &setup.shortcut.sound_music,                "shortcut.sound_music"
8760   },
8761   {
8762     TYPE_KEY_X11,
8763     &setup.shortcut.snap_left,                  "shortcut.snap_left"
8764   },
8765   {
8766     TYPE_KEY_X11,
8767     &setup.shortcut.snap_right,                 "shortcut.snap_right"
8768   },
8769   {
8770     TYPE_KEY_X11,
8771     &setup.shortcut.snap_up,                    "shortcut.snap_up"
8772   },
8773   {
8774     TYPE_KEY_X11,
8775     &setup.shortcut.snap_down,                  "shortcut.snap_down"
8776   },
8777 };
8778
8779 static struct SetupInputInfo setup_input;
8780 static struct TokenInfo player_setup_tokens[] =
8781 {
8782   {
8783     TYPE_BOOLEAN,
8784     &setup_input.use_joystick,                  ".use_joystick"
8785   },
8786   {
8787     TYPE_STRING,
8788     &setup_input.joy.device_name,               ".joy.device_name"
8789   },
8790   {
8791     TYPE_INTEGER,
8792     &setup_input.joy.xleft,                     ".joy.xleft"
8793   },
8794   {
8795     TYPE_INTEGER,
8796     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
8797   },
8798   {
8799     TYPE_INTEGER,
8800     &setup_input.joy.xright,                    ".joy.xright"
8801   },
8802   {
8803     TYPE_INTEGER,
8804     &setup_input.joy.yupper,                    ".joy.yupper"
8805   },
8806   {
8807     TYPE_INTEGER,
8808     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
8809   },
8810   {
8811     TYPE_INTEGER,
8812     &setup_input.joy.ylower,                    ".joy.ylower"
8813   },
8814   {
8815     TYPE_INTEGER,
8816     &setup_input.joy.snap,                      ".joy.snap_field"
8817   },
8818   {
8819     TYPE_INTEGER,
8820     &setup_input.joy.drop,                      ".joy.place_bomb"
8821   },
8822   {
8823     TYPE_KEY_X11,
8824     &setup_input.key.left,                      ".key.move_left"
8825   },
8826   {
8827     TYPE_KEY_X11,
8828     &setup_input.key.right,                     ".key.move_right"
8829   },
8830   {
8831     TYPE_KEY_X11,
8832     &setup_input.key.up,                        ".key.move_up"
8833   },
8834   {
8835     TYPE_KEY_X11,
8836     &setup_input.key.down,                      ".key.move_down"
8837   },
8838   {
8839     TYPE_KEY_X11,
8840     &setup_input.key.snap,                      ".key.snap_field"
8841   },
8842   {
8843     TYPE_KEY_X11,
8844     &setup_input.key.drop,                      ".key.place_bomb"
8845   },
8846 };
8847
8848 static struct TokenInfo system_setup_tokens[] =
8849 {
8850   {
8851     TYPE_STRING,
8852     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
8853   },
8854   {
8855     TYPE_STRING,
8856     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
8857   },
8858   {
8859     TYPE_INTEGER,
8860     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
8861   },
8862 };
8863
8864 static struct TokenInfo internal_setup_tokens[] =
8865 {
8866   {
8867     TYPE_STRING,
8868     &setup.internal.program_title,              "program_title"
8869   },
8870   {
8871     TYPE_STRING,
8872     &setup.internal.program_version,            "program_version"
8873   },
8874   {
8875     TYPE_STRING,
8876     &setup.internal.program_author,             "program_author"
8877   },
8878   {
8879     TYPE_STRING,
8880     &setup.internal.program_email,              "program_email"
8881   },
8882   {
8883     TYPE_STRING,
8884     &setup.internal.program_website,            "program_website"
8885   },
8886   {
8887     TYPE_STRING,
8888     &setup.internal.program_copyright,          "program_copyright"
8889   },
8890   {
8891     TYPE_STRING,
8892     &setup.internal.program_company,            "program_company"
8893   },
8894   {
8895     TYPE_STRING,
8896     &setup.internal.program_icon_file,          "program_icon_file"
8897   },
8898   {
8899     TYPE_STRING,
8900     &setup.internal.default_graphics_set,       "default_graphics_set"
8901   },
8902   {
8903     TYPE_STRING,
8904     &setup.internal.default_sounds_set,         "default_sounds_set"
8905   },
8906   {
8907     TYPE_STRING,
8908     &setup.internal.default_music_set,          "default_music_set"
8909   },
8910   {
8911     TYPE_STRING,
8912     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
8913   },
8914   {
8915     TYPE_STRING,
8916     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
8917   },
8918   {
8919     TYPE_STRING,
8920     &setup.internal.fallback_music_file,        "fallback_music_file"
8921   },
8922   {
8923     TYPE_STRING,
8924     &setup.internal.default_level_series,       "default_level_series"
8925   },
8926   {
8927     TYPE_INTEGER,
8928     &setup.internal.default_window_width,       "default_window_width"
8929   },
8930   {
8931     TYPE_INTEGER,
8932     &setup.internal.default_window_height,      "default_window_height"
8933   },
8934   {
8935     TYPE_BOOLEAN,
8936     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
8937   },
8938   {
8939     TYPE_BOOLEAN,
8940     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
8941   },
8942   {
8943     TYPE_BOOLEAN,
8944     &setup.internal.create_user_levelset,       "create_user_levelset"
8945   },
8946   {
8947     TYPE_BOOLEAN,
8948     &setup.internal.menu_game,                  "menu_game"
8949   },
8950   {
8951     TYPE_BOOLEAN,
8952     &setup.internal.menu_editor,                "menu_editor"
8953   },
8954   {
8955     TYPE_BOOLEAN,
8956     &setup.internal.menu_graphics,              "menu_graphics"
8957   },
8958   {
8959     TYPE_BOOLEAN,
8960     &setup.internal.menu_sound,                 "menu_sound"
8961   },
8962   {
8963     TYPE_BOOLEAN,
8964     &setup.internal.menu_artwork,               "menu_artwork"
8965   },
8966   {
8967     TYPE_BOOLEAN,
8968     &setup.internal.menu_input,                 "menu_input"
8969   },
8970   {
8971     TYPE_BOOLEAN,
8972     &setup.internal.menu_touch,                 "menu_touch"
8973   },
8974   {
8975     TYPE_BOOLEAN,
8976     &setup.internal.menu_shortcuts,             "menu_shortcuts"
8977   },
8978   {
8979     TYPE_BOOLEAN,
8980     &setup.internal.menu_exit,                  "menu_exit"
8981   },
8982   {
8983     TYPE_BOOLEAN,
8984     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
8985   },
8986 };
8987
8988 static struct TokenInfo debug_setup_tokens[] =
8989 {
8990   {
8991     TYPE_INTEGER,
8992     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
8993   },
8994   {
8995     TYPE_INTEGER,
8996     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
8997   },
8998   {
8999     TYPE_INTEGER,
9000     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
9001   },
9002   {
9003     TYPE_INTEGER,
9004     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
9005   },
9006   {
9007     TYPE_INTEGER,
9008     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
9009   },
9010   {
9011     TYPE_INTEGER,
9012     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
9013   },
9014   {
9015     TYPE_INTEGER,
9016     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
9017   },
9018   {
9019     TYPE_INTEGER,
9020     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
9021   },
9022   {
9023     TYPE_INTEGER,
9024     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
9025   },
9026   {
9027     TYPE_INTEGER,
9028     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
9029   },
9030   {
9031     TYPE_KEY_X11,
9032     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
9033   },
9034   {
9035     TYPE_KEY_X11,
9036     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
9037   },
9038   {
9039     TYPE_KEY_X11,
9040     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
9041   },
9042   {
9043     TYPE_KEY_X11,
9044     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
9045   },
9046   {
9047     TYPE_KEY_X11,
9048     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
9049   },
9050   {
9051     TYPE_KEY_X11,
9052     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
9053   },
9054   {
9055     TYPE_KEY_X11,
9056     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
9057   },
9058   {
9059     TYPE_KEY_X11,
9060     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
9061   },
9062   {
9063     TYPE_KEY_X11,
9064     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
9065   },
9066   {
9067     TYPE_KEY_X11,
9068     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
9069   },
9070   {
9071     TYPE_BOOLEAN,
9072     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
9073   {
9074     TYPE_BOOLEAN,
9075     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
9076   },
9077   {
9078     TYPE_BOOLEAN,
9079     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
9080   },
9081 };
9082
9083 static struct TokenInfo options_setup_tokens[] =
9084 {
9085   {
9086     TYPE_BOOLEAN,
9087     &setup.options.verbose,                     "options.verbose"
9088   },
9089 };
9090
9091 static char *get_corrected_login_name(char *login_name)
9092 {
9093   // needed because player name must be a fixed length string
9094   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
9095
9096   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
9097   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
9098
9099   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         // name has been cut
9100     if (strchr(login_name_new, ' '))
9101       *strchr(login_name_new, ' ') = '\0';
9102
9103   return login_name_new;
9104 }
9105
9106 static void setSetupInfoToDefaults(struct SetupInfo *si)
9107 {
9108   int i;
9109
9110   si->player_name = get_corrected_login_name(getLoginName());
9111
9112   si->sound = TRUE;
9113   si->sound_loops = TRUE;
9114   si->sound_music = TRUE;
9115   si->sound_simple = TRUE;
9116   si->toons = TRUE;
9117   si->scroll_delay = TRUE;
9118   si->forced_scroll_delay = FALSE;
9119   si->scroll_delay_value = STD_SCROLL_DELAY;
9120   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9121   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9122   si->fade_screens = TRUE;
9123   si->autorecord = TRUE;
9124   si->show_titlescreen = TRUE;
9125   si->quick_doors = FALSE;
9126   si->team_mode = FALSE;
9127   si->handicap = TRUE;
9128   si->skip_levels = TRUE;
9129   si->increment_levels = TRUE;
9130   si->auto_play_next_level = TRUE;
9131   si->skip_scores_after_game = FALSE;
9132   si->time_limit = TRUE;
9133   si->fullscreen = FALSE;
9134   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9135   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9136   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9137   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9138   si->ask_on_escape = TRUE;
9139   si->ask_on_escape_editor = TRUE;
9140   si->ask_on_game_over = TRUE;
9141   si->quick_switch = FALSE;
9142   si->input_on_focus = FALSE;
9143   si->prefer_aga_graphics = TRUE;
9144   si->game_speed_extended = FALSE;
9145   si->game_frame_delay = GAME_FRAME_DELAY;
9146   si->sp_show_border_elements = FALSE;
9147   si->small_game_graphics = FALSE;
9148   si->show_snapshot_buttons = FALSE;
9149
9150   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9151   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
9152   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
9153
9154   si->override_level_graphics = FALSE;
9155   si->override_level_sounds = FALSE;
9156   si->override_level_music = FALSE;
9157
9158   si->volume_simple = 100;              // percent
9159   si->volume_loops = 100;               // percent
9160   si->volume_music = 100;               // percent
9161
9162   si->network_mode = FALSE;
9163   si->network_player_nr = 0;            // first player
9164   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9165
9166   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9167   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
9168   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
9169   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
9170   si->touch.draw_outlined = TRUE;
9171   si->touch.draw_pressed = TRUE;
9172
9173   for (i = 0; i < 2; i++)
9174   {
9175     char *default_grid_button[6][2] =
9176     {
9177       { "      ", "  ^^  " },
9178       { "      ", "  ^^  " },
9179       { "      ", "<<  >>" },
9180       { "      ", "<<  >>" },
9181       { "111222", "  vv  " },
9182       { "111222", "  vv  " }
9183     };
9184     int grid_xsize = DEFAULT_GRID_XSIZE(i);
9185     int grid_ysize = DEFAULT_GRID_YSIZE(i);
9186     int min_xsize = MIN(6, grid_xsize);
9187     int min_ysize = MIN(6, grid_ysize);
9188     int startx = grid_xsize - min_xsize;
9189     int starty = grid_ysize - min_ysize;
9190     int x, y;
9191
9192     // virtual buttons grid can only be set to defaults if video is initialized
9193     // (this will be repeated if virtual buttons are not loaded from setup file)
9194     if (video.initialized)
9195     {
9196       si->touch.grid_xsize[i] = grid_xsize;
9197       si->touch.grid_ysize[i] = grid_ysize;
9198     }
9199     else
9200     {
9201       si->touch.grid_xsize[i] = -1;
9202       si->touch.grid_ysize[i] = -1;
9203     }
9204
9205     for (x = 0; x < MAX_GRID_XSIZE; x++)
9206       for (y = 0; y < MAX_GRID_YSIZE; y++)
9207         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9208
9209     for (x = 0; x < min_xsize; x++)
9210       for (y = 0; y < min_ysize; y++)
9211         si->touch.grid_button[i][x][starty + y] =
9212           default_grid_button[y][0][x];
9213
9214     for (x = 0; x < min_xsize; x++)
9215       for (y = 0; y < min_ysize; y++)
9216         si->touch.grid_button[i][startx + x][starty + y] =
9217           default_grid_button[y][1][x];
9218   }
9219
9220   si->touch.grid_initialized            = video.initialized;
9221
9222   si->editor.el_boulderdash             = TRUE;
9223   si->editor.el_emerald_mine            = TRUE;
9224   si->editor.el_emerald_mine_club       = TRUE;
9225   si->editor.el_more                    = TRUE;
9226   si->editor.el_sokoban                 = TRUE;
9227   si->editor.el_supaplex                = TRUE;
9228   si->editor.el_diamond_caves           = TRUE;
9229   si->editor.el_dx_boulderdash          = TRUE;
9230
9231   si->editor.el_mirror_magic            = TRUE;
9232   si->editor.el_deflektor               = TRUE;
9233
9234   si->editor.el_chars                   = TRUE;
9235   si->editor.el_steel_chars             = TRUE;
9236
9237   si->editor.el_classic                 = TRUE;
9238   si->editor.el_custom                  = TRUE;
9239
9240   si->editor.el_user_defined            = FALSE;
9241   si->editor.el_dynamic                 = TRUE;
9242
9243   si->editor.el_headlines               = TRUE;
9244
9245   si->editor.show_element_token         = FALSE;
9246
9247   si->editor.use_template_for_new_levels = TRUE;
9248
9249   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
9250   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
9251   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
9252
9253   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
9254   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
9255   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
9256   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
9257   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9258
9259   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
9260   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
9261   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
9262   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
9263   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
9264   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
9265
9266   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
9267   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
9268   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
9269
9270   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
9271   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
9272   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
9273   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
9274
9275   for (i = 0; i < MAX_PLAYERS; i++)
9276   {
9277     si->input[i].use_joystick = FALSE;
9278     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9279     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
9280     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9281     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
9282     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
9283     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9284     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
9285     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
9286     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
9287     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
9288     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9289     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
9290     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
9291     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
9292     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
9293   }
9294
9295   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9296   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9297   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9298
9299   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
9300   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
9301   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
9302   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
9303   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
9304   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9305   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
9306
9307   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9308
9309   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9310   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
9311   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
9312
9313   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9314   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
9315   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
9316
9317   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9318   si->internal.choose_from_top_leveldir = FALSE;
9319   si->internal.show_scaling_in_title = TRUE;
9320   si->internal.create_user_levelset = TRUE;
9321
9322   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
9323   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9324
9325   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9326   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9327   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9328   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9329   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9330   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9331   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9332   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9333   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9334   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9335
9336   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9337   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9338   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9339   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9340   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9341   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9342   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9343   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9344   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9345   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9346
9347   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9348   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
9349
9350   si->debug.show_frames_per_second = FALSE;
9351
9352   si->options.verbose = FALSE;
9353
9354 #if defined(PLATFORM_ANDROID)
9355   si->fullscreen = TRUE;
9356 #endif
9357 }
9358
9359 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9360 {
9361   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9362 }
9363
9364 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9365 {
9366   si->editor_cascade.el_bd              = TRUE;
9367   si->editor_cascade.el_em              = TRUE;
9368   si->editor_cascade.el_emc             = TRUE;
9369   si->editor_cascade.el_rnd             = TRUE;
9370   si->editor_cascade.el_sb              = TRUE;
9371   si->editor_cascade.el_sp              = TRUE;
9372   si->editor_cascade.el_dc              = TRUE;
9373   si->editor_cascade.el_dx              = TRUE;
9374
9375   si->editor_cascade.el_mm              = TRUE;
9376   si->editor_cascade.el_df              = TRUE;
9377
9378   si->editor_cascade.el_chars           = FALSE;
9379   si->editor_cascade.el_steel_chars     = FALSE;
9380   si->editor_cascade.el_ce              = FALSE;
9381   si->editor_cascade.el_ge              = FALSE;
9382   si->editor_cascade.el_ref             = FALSE;
9383   si->editor_cascade.el_user            = FALSE;
9384   si->editor_cascade.el_dynamic         = FALSE;
9385 }
9386
9387 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
9388
9389 static char *getHideSetupToken(void *setup_value)
9390 {
9391   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9392
9393   if (setup_value != NULL)
9394     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9395
9396   return hide_setup_token;
9397 }
9398
9399 void setHideSetupEntry(void *setup_value)
9400 {
9401   char *hide_setup_token = getHideSetupToken(setup_value);
9402
9403   if (setup_value != NULL)
9404     setHashEntry(hide_setup_hash, hide_setup_token, "");
9405 }
9406
9407 boolean hideSetupEntry(void *setup_value)
9408 {
9409   char *hide_setup_token = getHideSetupToken(setup_value);
9410
9411   return (setup_value != NULL &&
9412           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9413 }
9414
9415 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9416                                       struct TokenInfo *token_info,
9417                                       int token_nr, char *token_text)
9418 {
9419   char *token_hide_text = getStringCat2(token_text, ".hide");
9420   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9421
9422   // set the value of this setup option in the setup option structure
9423   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9424
9425   // check if this setup option should be hidden in the setup menu
9426   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9427     setHideSetupEntry(token_info[token_nr].value);
9428 }
9429
9430 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9431                                       struct TokenInfo *token_info,
9432                                       int token_nr)
9433 {
9434   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9435                             token_info[token_nr].text);
9436 }
9437
9438 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9439 {
9440   int i, pnr;
9441
9442   if (!setup_file_hash)
9443     return;
9444
9445   if (hide_setup_hash == NULL)
9446     hide_setup_hash = newSetupFileHash();
9447
9448   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9449     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9450
9451   setup.touch.grid_initialized = TRUE;
9452   for (i = 0; i < 2; i++)
9453   {
9454     int grid_xsize = setup.touch.grid_xsize[i];
9455     int grid_ysize = setup.touch.grid_ysize[i];
9456     int x, y;
9457
9458     // if virtual buttons are not loaded from setup file, repeat initializing
9459     // virtual buttons grid with default values later when video is initialized
9460     if (grid_xsize == -1 ||
9461         grid_ysize == -1)
9462     {
9463       setup.touch.grid_initialized = FALSE;
9464
9465       continue;
9466     }
9467
9468     for (y = 0; y < grid_ysize; y++)
9469     {
9470       char token_string[MAX_LINE_LEN];
9471
9472       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9473
9474       char *value_string = getHashEntry(setup_file_hash, token_string);
9475
9476       if (value_string == NULL)
9477         continue;
9478
9479       for (x = 0; x < grid_xsize; x++)
9480       {
9481         char c = value_string[x];
9482
9483         setup.touch.grid_button[i][x][y] =
9484           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9485       }
9486     }
9487   }
9488
9489   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9490     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9491
9492   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9493     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9494
9495   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9496   {
9497     char prefix[30];
9498
9499     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9500
9501     setup_input = setup.input[pnr];
9502     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9503     {
9504       char full_token[100];
9505
9506       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9507       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9508                                 full_token);
9509     }
9510     setup.input[pnr] = setup_input;
9511   }
9512
9513   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9514     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9515
9516   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9517     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9518
9519   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9520     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9521
9522   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9523     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9524
9525   setHideRelatedSetupEntries();
9526 }
9527
9528 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9529 {
9530   int i;
9531
9532   if (!setup_file_hash)
9533     return;
9534
9535   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9536     setSetupInfo(auto_setup_tokens, i,
9537                  getHashEntry(setup_file_hash,
9538                               auto_setup_tokens[i].text));
9539 }
9540
9541 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9542 {
9543   int i;
9544
9545   if (!setup_file_hash)
9546     return;
9547
9548   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9549     setSetupInfo(editor_cascade_setup_tokens, i,
9550                  getHashEntry(setup_file_hash,
9551                               editor_cascade_setup_tokens[i].text));
9552 }
9553
9554 void LoadSetupFromFilename(char *filename)
9555 {
9556   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9557
9558   if (setup_file_hash)
9559   {
9560     decodeSetupFileHash(setup_file_hash);
9561
9562     freeSetupFileHash(setup_file_hash);
9563   }
9564   else
9565   {
9566     Error(ERR_DEBUG, "using default setup values");
9567   }
9568 }
9569
9570 static void LoadSetup_SpecialPostProcessing(void)
9571 {
9572   char *player_name_new;
9573
9574   // needed to work around problems with fixed length strings
9575   player_name_new = get_corrected_login_name(setup.player_name);
9576   free(setup.player_name);
9577   setup.player_name = player_name_new;
9578
9579   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9580   if (setup.scroll_delay == FALSE)
9581   {
9582     setup.scroll_delay_value = MIN_SCROLL_DELAY;
9583     setup.scroll_delay = TRUE;                  // now always "on"
9584   }
9585
9586   // make sure that scroll delay value stays inside valid range
9587   setup.scroll_delay_value =
9588     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9589 }
9590
9591 void LoadSetup(void)
9592 {
9593   char *filename;
9594
9595   // always start with reliable default values
9596   setSetupInfoToDefaults(&setup);
9597
9598   // try to load setup values from default setup file
9599   filename = getDefaultSetupFilename();
9600
9601   if (fileExists(filename))
9602     LoadSetupFromFilename(filename);
9603
9604   // try to load setup values from user setup file
9605   filename = getSetupFilename();
9606
9607   LoadSetupFromFilename(filename);
9608
9609   LoadSetup_SpecialPostProcessing();
9610 }
9611
9612 void LoadSetup_AutoSetup(void)
9613 {
9614   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9615   SetupFileHash *setup_file_hash = NULL;
9616
9617   // always start with reliable default values
9618   setSetupInfoToDefaults_AutoSetup(&setup);
9619
9620   setup_file_hash = loadSetupFileHash(filename);
9621
9622   if (setup_file_hash)
9623   {
9624     decodeSetupFileHash_AutoSetup(setup_file_hash);
9625
9626     freeSetupFileHash(setup_file_hash);
9627   }
9628
9629   free(filename);
9630 }
9631
9632 void LoadSetup_EditorCascade(void)
9633 {
9634   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9635   SetupFileHash *setup_file_hash = NULL;
9636
9637   // always start with reliable default values
9638   setSetupInfoToDefaults_EditorCascade(&setup);
9639
9640   setup_file_hash = loadSetupFileHash(filename);
9641
9642   if (setup_file_hash)
9643   {
9644     decodeSetupFileHash_EditorCascade(setup_file_hash);
9645
9646     freeSetupFileHash(setup_file_hash);
9647   }
9648
9649   free(filename);
9650 }
9651
9652 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9653                                            char *mapping_line)
9654 {
9655   char mapping_guid[MAX_LINE_LEN];
9656   char *mapping_start, *mapping_end;
9657
9658   // get GUID from game controller mapping line: copy complete line
9659   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9660   mapping_guid[MAX_LINE_LEN - 1] = '\0';
9661
9662   // get GUID from game controller mapping line: cut after GUID part
9663   mapping_start = strchr(mapping_guid, ',');
9664   if (mapping_start != NULL)
9665     *mapping_start = '\0';
9666
9667   // cut newline from game controller mapping line
9668   mapping_end = strchr(mapping_line, '\n');
9669   if (mapping_end != NULL)
9670     *mapping_end = '\0';
9671
9672   // add mapping entry to game controller mappings hash
9673   setHashEntry(mappings_hash, mapping_guid, mapping_line);
9674 }
9675
9676 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9677                                                  char *filename)
9678 {
9679   FILE *file;
9680
9681   if (!(file = fopen(filename, MODE_READ)))
9682   {
9683     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9684
9685     return;
9686   }
9687
9688   while (!feof(file))
9689   {
9690     char line[MAX_LINE_LEN];
9691
9692     if (!fgets(line, MAX_LINE_LEN, file))
9693       break;
9694
9695     addGameControllerMappingToHash(mappings_hash, line);
9696   }
9697
9698   fclose(file);
9699 }
9700
9701 void SaveSetup(void)
9702 {
9703   char *filename = getSetupFilename();
9704   FILE *file;
9705   int i, pnr;
9706
9707   InitUserDataDirectory();
9708
9709   if (!(file = fopen(filename, MODE_WRITE)))
9710   {
9711     Error(ERR_WARN, "cannot write setup file '%s'", filename);
9712     return;
9713   }
9714
9715   fprintFileHeader(file, SETUP_FILENAME);
9716
9717   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9718   {
9719     // just to make things nicer :)
9720     if (global_setup_tokens[i].value == &setup.sound                    ||
9721         global_setup_tokens[i].value == &setup.graphics_set             ||
9722         global_setup_tokens[i].value == &setup.volume_simple            ||
9723         global_setup_tokens[i].value == &setup.network_mode             ||
9724         global_setup_tokens[i].value == &setup.touch.control_type       ||
9725         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
9726         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9727       fprintf(file, "\n");
9728
9729     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9730   }
9731
9732   for (i = 0; i < 2; i++)
9733   {
9734     int grid_xsize = setup.touch.grid_xsize[i];
9735     int grid_ysize = setup.touch.grid_ysize[i];
9736     int x, y;
9737
9738     fprintf(file, "\n");
9739
9740     for (y = 0; y < grid_ysize; y++)
9741     {
9742       char token_string[MAX_LINE_LEN];
9743       char value_string[MAX_LINE_LEN];
9744
9745       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9746
9747       for (x = 0; x < grid_xsize; x++)
9748       {
9749         char c = setup.touch.grid_button[i][x][y];
9750
9751         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9752       }
9753
9754       value_string[grid_xsize] = '\0';
9755
9756       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9757     }
9758   }
9759
9760   fprintf(file, "\n");
9761   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9762     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9763
9764   fprintf(file, "\n");
9765   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9766     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9767
9768   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9769   {
9770     char prefix[30];
9771
9772     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9773     fprintf(file, "\n");
9774
9775     setup_input = setup.input[pnr];
9776     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9777       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9778   }
9779
9780   fprintf(file, "\n");
9781   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9782     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9783
9784   // (internal setup values not saved to user setup file)
9785
9786   fprintf(file, "\n");
9787   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9788     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9789
9790   fprintf(file, "\n");
9791   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9792     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9793
9794   fclose(file);
9795
9796   SetFilePermissions(filename, PERMS_PRIVATE);
9797 }
9798
9799 void SaveSetup_AutoSetup(void)
9800 {
9801   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9802   FILE *file;
9803   int i;
9804
9805   InitUserDataDirectory();
9806
9807   if (!(file = fopen(filename, MODE_WRITE)))
9808   {
9809     Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9810     free(filename);
9811     return;
9812   }
9813
9814   fprintFileHeader(file, AUTOSETUP_FILENAME);
9815
9816   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9817     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9818
9819   fclose(file);
9820
9821   SetFilePermissions(filename, PERMS_PRIVATE);
9822
9823   free(filename);
9824 }
9825
9826 void SaveSetup_EditorCascade(void)
9827 {
9828   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9829   FILE *file;
9830   int i;
9831
9832   InitUserDataDirectory();
9833
9834   if (!(file = fopen(filename, MODE_WRITE)))
9835   {
9836     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9837     free(filename);
9838     return;
9839   }
9840
9841   fprintFileHeader(file, EDITORCASCADE_FILENAME);
9842
9843   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9844     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9845
9846   fclose(file);
9847
9848   SetFilePermissions(filename, PERMS_PRIVATE);
9849
9850   free(filename);
9851 }
9852
9853 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9854                                                   char *filename)
9855 {
9856   FILE *file;
9857
9858   if (!(file = fopen(filename, MODE_WRITE)))
9859   {
9860     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9861
9862     return;
9863   }
9864
9865   BEGIN_HASH_ITERATION(mappings_hash, itr)
9866   {
9867     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9868   }
9869   END_HASH_ITERATION(mappings_hash, itr)
9870
9871   fclose(file);
9872 }
9873
9874 void SaveSetup_AddGameControllerMapping(char *mapping)
9875 {
9876   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9877   SetupFileHash *mappings_hash = newSetupFileHash();
9878
9879   InitUserDataDirectory();
9880
9881   // load existing personal game controller mappings
9882   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9883
9884   // add new mapping to personal game controller mappings
9885   addGameControllerMappingToHash(mappings_hash, mapping);
9886
9887   // save updated personal game controller mappings
9888   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9889
9890   freeSetupFileHash(mappings_hash);
9891   free(filename);
9892 }
9893
9894 void LoadCustomElementDescriptions(void)
9895 {
9896   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9897   SetupFileHash *setup_file_hash;
9898   int i;
9899
9900   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9901   {
9902     if (element_info[i].custom_description != NULL)
9903     {
9904       free(element_info[i].custom_description);
9905       element_info[i].custom_description = NULL;
9906     }
9907   }
9908
9909   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9910     return;
9911
9912   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9913   {
9914     char *token = getStringCat2(element_info[i].token_name, ".name");
9915     char *value = getHashEntry(setup_file_hash, token);
9916
9917     if (value != NULL)
9918       element_info[i].custom_description = getStringCopy(value);
9919
9920     free(token);
9921   }
9922
9923   freeSetupFileHash(setup_file_hash);
9924 }
9925
9926 static int getElementFromToken(char *token)
9927 {
9928   char *value = getHashEntry(element_token_hash, token);
9929
9930   if (value != NULL)
9931     return atoi(value);
9932
9933   Error(ERR_WARN, "unknown element token '%s'", token);
9934
9935   return EL_UNDEFINED;
9936 }
9937
9938 void FreeGlobalAnimEventInfo(void)
9939 {
9940   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9941
9942   if (gaei->event_list == NULL)
9943     return;
9944
9945   int i;
9946
9947   for (i = 0; i < gaei->num_event_lists; i++)
9948   {
9949     checked_free(gaei->event_list[i]->event_value);
9950     checked_free(gaei->event_list[i]);
9951   }
9952
9953   checked_free(gaei->event_list);
9954
9955   gaei->event_list = NULL;
9956   gaei->num_event_lists = 0;
9957 }
9958
9959 static int AddGlobalAnimEventList(void)
9960 {
9961   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9962   int list_pos = gaei->num_event_lists++;
9963
9964   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
9965                                      sizeof(struct GlobalAnimEventListInfo *));
9966
9967   gaei->event_list[list_pos] =
9968     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
9969
9970   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
9971
9972   gaeli->event_value = NULL;
9973   gaeli->num_event_values = 0;
9974
9975   return list_pos;
9976 }
9977
9978 static int AddGlobalAnimEventValue(int list_pos, int event_value)
9979 {
9980   // do not add empty global animation events
9981   if (event_value == ANIM_EVENT_NONE)
9982     return list_pos;
9983
9984   // if list position is undefined, create new list
9985   if (list_pos == ANIM_EVENT_UNDEFINED)
9986     list_pos = AddGlobalAnimEventList();
9987
9988   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9989   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
9990   int value_pos = gaeli->num_event_values++;
9991
9992   gaeli->event_value = checked_realloc(gaeli->event_value,
9993                                        gaeli->num_event_values * sizeof(int *));
9994
9995   gaeli->event_value[value_pos] = event_value;
9996
9997   return list_pos;
9998 }
9999
10000 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10001 {
10002   if (list_pos == ANIM_EVENT_UNDEFINED)
10003     return ANIM_EVENT_NONE;
10004
10005   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10006   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10007
10008   return gaeli->event_value[value_pos];
10009 }
10010
10011 int GetGlobalAnimEventValueCount(int list_pos)
10012 {
10013   if (list_pos == ANIM_EVENT_UNDEFINED)
10014     return 0;
10015
10016   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10017   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10018
10019   return gaeli->num_event_values;
10020 }
10021
10022 // This function checks if a string <s> of the format "string1, string2, ..."
10023 // exactly contains a string <s_contained>.
10024
10025 static boolean string_has_parameter(char *s, char *s_contained)
10026 {
10027   char *substring;
10028
10029   if (s == NULL || s_contained == NULL)
10030     return FALSE;
10031
10032   if (strlen(s_contained) > strlen(s))
10033     return FALSE;
10034
10035   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10036   {
10037     char next_char = s[strlen(s_contained)];
10038
10039     // check if next character is delimiter or whitespace
10040     return (next_char == ',' || next_char == '\0' ||
10041             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10042   }
10043
10044   // check if string contains another parameter string after a comma
10045   substring = strchr(s, ',');
10046   if (substring == NULL)        // string does not contain a comma
10047     return FALSE;
10048
10049   // advance string pointer to next character after the comma
10050   substring++;
10051
10052   // skip potential whitespaces after the comma
10053   while (*substring == ' ' || *substring == '\t')
10054     substring++;
10055
10056   return string_has_parameter(substring, s_contained);
10057 }
10058
10059 static int get_anim_parameter_value(char *s)
10060 {
10061   int event_value[] =
10062   {
10063     ANIM_EVENT_CLICK,
10064     ANIM_EVENT_INIT,
10065     ANIM_EVENT_START,
10066     ANIM_EVENT_END,
10067     ANIM_EVENT_POST
10068   };
10069   char *pattern_1[] =
10070   {
10071     "click:anim_",
10072     "init:anim_",
10073     "start:anim_",
10074     "end:anim_",
10075     "post:anim_"
10076   };
10077   char *pattern_2 = ".part_";
10078   char *matching_char = NULL;
10079   char *s_ptr = s;
10080   int pattern_1_len = 0;
10081   int result = ANIM_EVENT_NONE;
10082   int i;
10083
10084   for (i = 0; i < ARRAY_SIZE(event_value); i++)
10085   {
10086     matching_char = strstr(s_ptr, pattern_1[i]);
10087     pattern_1_len = strlen(pattern_1[i]);
10088     result = event_value[i];
10089
10090     if (matching_char != NULL)
10091       break;
10092   }
10093
10094   if (matching_char == NULL)
10095     return ANIM_EVENT_NONE;
10096
10097   s_ptr = matching_char + pattern_1_len;
10098
10099   // check for main animation number ("anim_X" or "anim_XX")
10100   if (*s_ptr >= '0' && *s_ptr <= '9')
10101   {
10102     int gic_anim_nr = (*s_ptr++ - '0');
10103
10104     if (*s_ptr >= '0' && *s_ptr <= '9')
10105       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10106
10107     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10108       return ANIM_EVENT_NONE;
10109
10110     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10111   }
10112   else
10113   {
10114     // invalid main animation number specified
10115
10116     return ANIM_EVENT_NONE;
10117   }
10118
10119   // check for animation part number ("part_X" or "part_XX") (optional)
10120   if (strPrefix(s_ptr, pattern_2))
10121   {
10122     s_ptr += strlen(pattern_2);
10123
10124     if (*s_ptr >= '0' && *s_ptr <= '9')
10125     {
10126       int gic_part_nr = (*s_ptr++ - '0');
10127
10128       if (*s_ptr >= '0' && *s_ptr <= '9')
10129         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10130
10131       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10132         return ANIM_EVENT_NONE;
10133
10134       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10135     }
10136     else
10137     {
10138       // invalid animation part number specified
10139
10140       return ANIM_EVENT_NONE;
10141     }
10142   }
10143
10144   // discard result if next character is neither delimiter nor whitespace
10145   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10146         *s_ptr == ' ' || *s_ptr == '\t'))
10147     return ANIM_EVENT_NONE;
10148
10149   return result;
10150 }
10151
10152 static int get_anim_parameter_values(char *s)
10153 {
10154   int list_pos = ANIM_EVENT_UNDEFINED;
10155   int event_value = ANIM_EVENT_DEFAULT;
10156
10157   if (string_has_parameter(s, "any"))
10158     event_value |= ANIM_EVENT_ANY;
10159
10160   if (string_has_parameter(s, "click:self") ||
10161       string_has_parameter(s, "click") ||
10162       string_has_parameter(s, "self"))
10163     event_value |= ANIM_EVENT_SELF;
10164
10165   if (string_has_parameter(s, "unclick:any"))
10166     event_value |= ANIM_EVENT_UNCLICK_ANY;
10167
10168   // if animation event found, add it to global animation event list
10169   if (event_value != ANIM_EVENT_NONE)
10170     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10171
10172   while (s != NULL)
10173   {
10174     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10175     event_value = get_anim_parameter_value(s);
10176
10177     // if animation event found, add it to global animation event list
10178     if (event_value != ANIM_EVENT_NONE)
10179       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10180
10181     // continue with next part of the string, starting with next comma
10182     s = strchr(s + 1, ',');
10183   }
10184
10185   return list_pos;
10186 }
10187
10188 static int get_anim_action_parameter_value(char *token)
10189 {
10190   int result = getImageIDFromToken(token);
10191
10192   if (result == -1)
10193   {
10194     char *gfx_token = getStringCat2("gfx.", token);
10195
10196     result = getImageIDFromToken(gfx_token);
10197
10198     checked_free(gfx_token);
10199   }
10200
10201   if (result == -1)
10202   {
10203     Key key = getKeyFromX11KeyName(token);
10204
10205     if (key != KSYM_UNDEFINED)
10206       result = -(int)key;
10207   }
10208
10209   if (result == -1)
10210     result = ANIM_EVENT_ACTION_NONE;
10211
10212   return result;
10213 }
10214
10215 int get_parameter_value(char *value_raw, char *suffix, int type)
10216 {
10217   char *value = getStringToLower(value_raw);
10218   int result = 0;       // probably a save default value
10219
10220   if (strEqual(suffix, ".direction"))
10221   {
10222     result = (strEqual(value, "left")  ? MV_LEFT :
10223               strEqual(value, "right") ? MV_RIGHT :
10224               strEqual(value, "up")    ? MV_UP :
10225               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
10226   }
10227   else if (strEqual(suffix, ".position"))
10228   {
10229     result = (strEqual(value, "left")   ? POS_LEFT :
10230               strEqual(value, "right")  ? POS_RIGHT :
10231               strEqual(value, "top")    ? POS_TOP :
10232               strEqual(value, "upper")  ? POS_UPPER :
10233               strEqual(value, "middle") ? POS_MIDDLE :
10234               strEqual(value, "lower")  ? POS_LOWER :
10235               strEqual(value, "bottom") ? POS_BOTTOM :
10236               strEqual(value, "any")    ? POS_ANY :
10237               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
10238   }
10239   else if (strEqual(suffix, ".align"))
10240   {
10241     result = (strEqual(value, "left")   ? ALIGN_LEFT :
10242               strEqual(value, "right")  ? ALIGN_RIGHT :
10243               strEqual(value, "center") ? ALIGN_CENTER :
10244               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10245   }
10246   else if (strEqual(suffix, ".valign"))
10247   {
10248     result = (strEqual(value, "top")    ? VALIGN_TOP :
10249               strEqual(value, "bottom") ? VALIGN_BOTTOM :
10250               strEqual(value, "middle") ? VALIGN_MIDDLE :
10251               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10252   }
10253   else if (strEqual(suffix, ".anim_mode"))
10254   {
10255     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
10256               string_has_parameter(value, "loop")       ? ANIM_LOOP :
10257               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
10258               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
10259               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
10260               string_has_parameter(value, "random")     ? ANIM_RANDOM :
10261               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
10262               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
10263               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
10264               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10265               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
10266               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
10267               string_has_parameter(value, "all")        ? ANIM_ALL :
10268               ANIM_DEFAULT);
10269
10270     if (string_has_parameter(value, "once"))
10271       result |= ANIM_ONCE;
10272
10273     if (string_has_parameter(value, "reverse"))
10274       result |= ANIM_REVERSE;
10275
10276     if (string_has_parameter(value, "opaque_player"))
10277       result |= ANIM_OPAQUE_PLAYER;
10278
10279     if (string_has_parameter(value, "static_panel"))
10280       result |= ANIM_STATIC_PANEL;
10281   }
10282   else if (strEqual(suffix, ".init_event") ||
10283            strEqual(suffix, ".anim_event"))
10284   {
10285     result = get_anim_parameter_values(value);
10286   }
10287   else if (strEqual(suffix, ".init_delay_action") ||
10288            strEqual(suffix, ".anim_delay_action") ||
10289            strEqual(suffix, ".post_delay_action") ||
10290            strEqual(suffix, ".init_event_action") ||
10291            strEqual(suffix, ".anim_event_action"))
10292   {
10293     result = get_anim_action_parameter_value(value_raw);
10294   }
10295   else if (strEqual(suffix, ".class"))
10296   {
10297     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10298               get_hash_from_key(value));
10299   }
10300   else if (strEqual(suffix, ".style"))
10301   {
10302     result = STYLE_DEFAULT;
10303
10304     if (string_has_parameter(value, "accurate_borders"))
10305       result |= STYLE_ACCURATE_BORDERS;
10306
10307     if (string_has_parameter(value, "inner_corners"))
10308       result |= STYLE_INNER_CORNERS;
10309
10310     if (string_has_parameter(value, "reverse"))
10311       result |= STYLE_REVERSE;
10312
10313     if (string_has_parameter(value, "block_clicks"))
10314       result |= STYLE_BLOCK;
10315
10316     if (string_has_parameter(value, "passthrough_clicks"))
10317       result |= STYLE_PASSTHROUGH;
10318
10319     if (string_has_parameter(value, "multiple_actions"))
10320       result |= STYLE_MULTIPLE_ACTIONS;
10321   }
10322   else if (strEqual(suffix, ".fade_mode"))
10323   {
10324     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
10325               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
10326               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
10327               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
10328               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
10329               FADE_MODE_DEFAULT);
10330   }
10331   else if (strEqual(suffix, ".auto_delay_unit"))
10332   {
10333     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
10334               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10335               AUTO_DELAY_UNIT_DEFAULT);
10336   }
10337   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
10338   {
10339     result = gfx.get_font_from_token_function(value);
10340   }
10341   else          // generic parameter of type integer or boolean
10342   {
10343     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10344               type == TYPE_INTEGER ? get_integer_from_string(value) :
10345               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10346               ARG_UNDEFINED_VALUE);
10347   }
10348
10349   free(value);
10350
10351   return result;
10352 }
10353
10354 static int get_token_parameter_value(char *token, char *value_raw)
10355 {
10356   char *suffix;
10357
10358   if (token == NULL || value_raw == NULL)
10359     return ARG_UNDEFINED_VALUE;
10360
10361   suffix = strrchr(token, '.');
10362   if (suffix == NULL)
10363     suffix = token;
10364
10365   if (strEqual(suffix, ".element"))
10366     return getElementFromToken(value_raw);
10367
10368   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10369   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10370 }
10371
10372 void InitMenuDesignSettings_Static(void)
10373 {
10374   int i;
10375
10376   // always start with reliable default values from static default config
10377   for (i = 0; image_config_vars[i].token != NULL; i++)
10378   {
10379     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10380
10381     if (value != NULL)
10382       *image_config_vars[i].value =
10383         get_token_parameter_value(image_config_vars[i].token, value);
10384   }
10385 }
10386
10387 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10388 {
10389   int i;
10390
10391   // the following initializes hierarchical values from static configuration
10392
10393   // special case: initialize "ARG_DEFAULT" values in static default config
10394   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10395   titlescreen_initial_first_default.fade_mode  =
10396     title_initial_first_default.fade_mode;
10397   titlescreen_initial_first_default.fade_delay =
10398     title_initial_first_default.fade_delay;
10399   titlescreen_initial_first_default.post_delay =
10400     title_initial_first_default.post_delay;
10401   titlescreen_initial_first_default.auto_delay =
10402     title_initial_first_default.auto_delay;
10403   titlescreen_initial_first_default.auto_delay_unit =
10404     title_initial_first_default.auto_delay_unit;
10405   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
10406   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10407   titlescreen_first_default.post_delay = title_first_default.post_delay;
10408   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10409   titlescreen_first_default.auto_delay_unit =
10410     title_first_default.auto_delay_unit;
10411   titlemessage_initial_first_default.fade_mode  =
10412     title_initial_first_default.fade_mode;
10413   titlemessage_initial_first_default.fade_delay =
10414     title_initial_first_default.fade_delay;
10415   titlemessage_initial_first_default.post_delay =
10416     title_initial_first_default.post_delay;
10417   titlemessage_initial_first_default.auto_delay =
10418     title_initial_first_default.auto_delay;
10419   titlemessage_initial_first_default.auto_delay_unit =
10420     title_initial_first_default.auto_delay_unit;
10421   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
10422   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10423   titlemessage_first_default.post_delay = title_first_default.post_delay;
10424   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10425   titlemessage_first_default.auto_delay_unit =
10426     title_first_default.auto_delay_unit;
10427
10428   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
10429   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10430   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10431   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10432   titlescreen_initial_default.auto_delay_unit =
10433     title_initial_default.auto_delay_unit;
10434   titlescreen_default.fade_mode  = title_default.fade_mode;
10435   titlescreen_default.fade_delay = title_default.fade_delay;
10436   titlescreen_default.post_delay = title_default.post_delay;
10437   titlescreen_default.auto_delay = title_default.auto_delay;
10438   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10439   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
10440   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10441   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10442   titlemessage_initial_default.auto_delay_unit =
10443     title_initial_default.auto_delay_unit;
10444   titlemessage_default.fade_mode  = title_default.fade_mode;
10445   titlemessage_default.fade_delay = title_default.fade_delay;
10446   titlemessage_default.post_delay = title_default.post_delay;
10447   titlemessage_default.auto_delay = title_default.auto_delay;
10448   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10449
10450   // special case: initialize "ARG_DEFAULT" values in static default config
10451   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10452   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10453   {
10454     titlescreen_initial_first[i] = titlescreen_initial_first_default;
10455     titlescreen_first[i] = titlescreen_first_default;
10456     titlemessage_initial_first[i] = titlemessage_initial_first_default;
10457     titlemessage_first[i] = titlemessage_first_default;
10458
10459     titlescreen_initial[i] = titlescreen_initial_default;
10460     titlescreen[i] = titlescreen_default;
10461     titlemessage_initial[i] = titlemessage_initial_default;
10462     titlemessage[i] = titlemessage_default;
10463   }
10464
10465   // special case: initialize "ARG_DEFAULT" values in static default config
10466   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10467   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10468   {
10469     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
10470       continue;
10471
10472     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10473     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10474     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10475   }
10476
10477   // special case: initialize "ARG_DEFAULT" values in static default config
10478   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10479   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10480   {
10481     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10482     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10483     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10484
10485     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
10486       continue;
10487
10488     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10489   }
10490 }
10491
10492 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10493 {
10494   static struct
10495   {
10496     struct XY *dst, *src;
10497   }
10498   game_buttons_xy[] =
10499   {
10500     { &game.button.save,        &game.button.stop       },
10501     { &game.button.pause2,      &game.button.pause      },
10502     { &game.button.load,        &game.button.play       },
10503     { &game.button.undo,        &game.button.stop       },
10504     { &game.button.redo,        &game.button.play       },
10505
10506     { NULL,                     NULL                    }
10507   };
10508   int i, j;
10509
10510   // special case: initialize later added SETUP list size from LEVELS value
10511   if (menu.list_size[GAME_MODE_SETUP] == -1)
10512     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10513
10514   // set default position for snapshot buttons to stop/pause/play buttons
10515   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10516     if ((*game_buttons_xy[i].dst).x == -1 &&
10517         (*game_buttons_xy[i].dst).y == -1)
10518       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10519
10520   // --------------------------------------------------------------------------
10521   // dynamic viewports (including playfield margins, borders and alignments)
10522   // --------------------------------------------------------------------------
10523
10524   // dynamic viewports currently only supported for landscape mode
10525   int display_width  = MAX(video.display_width, video.display_height);
10526   int display_height = MIN(video.display_width, video.display_height);
10527
10528   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10529   {
10530     struct RectWithBorder *vp_window    = &viewport.window[i];
10531     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10532     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
10533     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
10534     boolean dynamic_window_width     = (vp_window->min_width     != -1);
10535     boolean dynamic_window_height    = (vp_window->min_height    != -1);
10536     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
10537     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10538
10539     // adjust window size if min/max width/height is specified
10540
10541     if (vp_window->min_width != -1)
10542     {
10543       int window_width = display_width;
10544
10545       // when using static window height, use aspect ratio of display
10546       if (vp_window->min_height == -1)
10547         window_width = vp_window->height * display_width / display_height;
10548
10549       vp_window->width = MAX(vp_window->min_width, window_width);
10550     }
10551
10552     if (vp_window->min_height != -1)
10553     {
10554       int window_height = display_height;
10555
10556       // when using static window width, use aspect ratio of display
10557       if (vp_window->min_width == -1)
10558         window_height = vp_window->width * display_height / display_width;
10559
10560       vp_window->height = MAX(vp_window->min_height, window_height);
10561     }
10562
10563     if (vp_window->max_width != -1)
10564       vp_window->width = MIN(vp_window->width, vp_window->max_width);
10565
10566     if (vp_window->max_height != -1)
10567       vp_window->height = MIN(vp_window->height, vp_window->max_height);
10568
10569     int playfield_width  = vp_window->width;
10570     int playfield_height = vp_window->height;
10571
10572     // adjust playfield size and position according to specified margins
10573
10574     playfield_width  -= vp_playfield->margin_left;
10575     playfield_width  -= vp_playfield->margin_right;
10576
10577     playfield_height -= vp_playfield->margin_top;
10578     playfield_height -= vp_playfield->margin_bottom;
10579
10580     // adjust playfield size if min/max width/height is specified
10581
10582     if (vp_playfield->min_width != -1)
10583       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10584
10585     if (vp_playfield->min_height != -1)
10586       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10587
10588     if (vp_playfield->max_width != -1)
10589       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10590
10591     if (vp_playfield->max_height != -1)
10592       vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10593
10594     // adjust playfield position according to specified alignment
10595
10596     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10597       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10598     else if (vp_playfield->align == ALIGN_CENTER)
10599       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10600     else if (vp_playfield->align == ALIGN_RIGHT)
10601       vp_playfield->x += playfield_width - vp_playfield->width;
10602
10603     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10604       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10605     else if (vp_playfield->valign == VALIGN_MIDDLE)
10606       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10607     else if (vp_playfield->valign == VALIGN_BOTTOM)
10608       vp_playfield->y += playfield_height - vp_playfield->height;
10609
10610     vp_playfield->x += vp_playfield->margin_left;
10611     vp_playfield->y += vp_playfield->margin_top;
10612
10613     // adjust individual playfield borders if only default border is specified
10614
10615     if (vp_playfield->border_left == -1)
10616       vp_playfield->border_left = vp_playfield->border_size;
10617     if (vp_playfield->border_right == -1)
10618       vp_playfield->border_right = vp_playfield->border_size;
10619     if (vp_playfield->border_top == -1)
10620       vp_playfield->border_top = vp_playfield->border_size;
10621     if (vp_playfield->border_bottom == -1)
10622       vp_playfield->border_bottom = vp_playfield->border_size;
10623
10624     // set dynamic playfield borders if borders are specified as undefined
10625     // (but only if window size was dynamic and playfield size was static)
10626
10627     if (dynamic_window_width && !dynamic_playfield_width)
10628     {
10629       if (vp_playfield->border_left == -1)
10630       {
10631         vp_playfield->border_left = (vp_playfield->x -
10632                                      vp_playfield->margin_left);
10633         vp_playfield->x     -= vp_playfield->border_left;
10634         vp_playfield->width += vp_playfield->border_left;
10635       }
10636
10637       if (vp_playfield->border_right == -1)
10638       {
10639         vp_playfield->border_right = (vp_window->width -
10640                                       vp_playfield->x -
10641                                       vp_playfield->width -
10642                                       vp_playfield->margin_right);
10643         vp_playfield->width += vp_playfield->border_right;
10644       }
10645     }
10646
10647     if (dynamic_window_height && !dynamic_playfield_height)
10648     {
10649       if (vp_playfield->border_top == -1)
10650       {
10651         vp_playfield->border_top = (vp_playfield->y -
10652                                     vp_playfield->margin_top);
10653         vp_playfield->y      -= vp_playfield->border_top;
10654         vp_playfield->height += vp_playfield->border_top;
10655       }
10656
10657       if (vp_playfield->border_bottom == -1)
10658       {
10659         vp_playfield->border_bottom = (vp_window->height -
10660                                        vp_playfield->y -
10661                                        vp_playfield->height -
10662                                        vp_playfield->margin_bottom);
10663         vp_playfield->height += vp_playfield->border_bottom;
10664       }
10665     }
10666
10667     // adjust playfield size to be a multiple of a defined alignment tile size
10668
10669     int align_size = vp_playfield->align_size;
10670     int playfield_xtiles = vp_playfield->width  / align_size;
10671     int playfield_ytiles = vp_playfield->height / align_size;
10672     int playfield_width_corrected  = playfield_xtiles * align_size;
10673     int playfield_height_corrected = playfield_ytiles * align_size;
10674     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10675                                  i == GFX_SPECIAL_ARG_EDITOR);
10676
10677     if (is_playfield_mode &&
10678         dynamic_playfield_width &&
10679         vp_playfield->width != playfield_width_corrected)
10680     {
10681       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10682
10683       vp_playfield->width = playfield_width_corrected;
10684
10685       if (vp_playfield->align == ALIGN_LEFT)
10686       {
10687         vp_playfield->border_left += playfield_xdiff;
10688       }
10689       else if (vp_playfield->align == ALIGN_RIGHT)
10690       {
10691         vp_playfield->border_right += playfield_xdiff;
10692       }
10693       else if (vp_playfield->align == ALIGN_CENTER)
10694       {
10695         int border_left_diff  = playfield_xdiff / 2;
10696         int border_right_diff = playfield_xdiff - border_left_diff;
10697
10698         vp_playfield->border_left  += border_left_diff;
10699         vp_playfield->border_right += border_right_diff;
10700       }
10701     }
10702
10703     if (is_playfield_mode &&
10704         dynamic_playfield_height &&
10705         vp_playfield->height != playfield_height_corrected)
10706     {
10707       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10708
10709       vp_playfield->height = playfield_height_corrected;
10710
10711       if (vp_playfield->valign == VALIGN_TOP)
10712       {
10713         vp_playfield->border_top += playfield_ydiff;
10714       }
10715       else if (vp_playfield->align == VALIGN_BOTTOM)
10716       {
10717         vp_playfield->border_right += playfield_ydiff;
10718       }
10719       else if (vp_playfield->align == VALIGN_MIDDLE)
10720       {
10721         int border_top_diff    = playfield_ydiff / 2;
10722         int border_bottom_diff = playfield_ydiff - border_top_diff;
10723
10724         vp_playfield->border_top    += border_top_diff;
10725         vp_playfield->border_bottom += border_bottom_diff;
10726       }
10727     }
10728
10729     // adjust door positions according to specified alignment
10730
10731     for (j = 0; j < 2; j++)
10732     {
10733       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10734
10735       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10736         vp_door->x = ALIGNED_VP_XPOS(vp_door);
10737       else if (vp_door->align == ALIGN_CENTER)
10738         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10739       else if (vp_door->align == ALIGN_RIGHT)
10740         vp_door->x += vp_window->width - vp_door->width;
10741
10742       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10743         vp_door->y = ALIGNED_VP_YPOS(vp_door);
10744       else if (vp_door->valign == VALIGN_MIDDLE)
10745         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10746       else if (vp_door->valign == VALIGN_BOTTOM)
10747         vp_door->y += vp_window->height - vp_door->height;
10748     }
10749   }
10750 }
10751
10752 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10753 {
10754   static struct
10755   {
10756     struct XYTileSize *dst, *src;
10757     int graphic;
10758   }
10759   editor_buttons_xy[] =
10760   {
10761     {
10762       &editor.button.element_left,      &editor.palette.element_left,
10763       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10764     },
10765     {
10766       &editor.button.element_middle,    &editor.palette.element_middle,
10767       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10768     },
10769     {
10770       &editor.button.element_right,     &editor.palette.element_right,
10771       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10772     },
10773
10774     { NULL,                     NULL                    }
10775   };
10776   int i;
10777
10778   // set default position for element buttons to element graphics
10779   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10780   {
10781     if ((*editor_buttons_xy[i].dst).x == -1 &&
10782         (*editor_buttons_xy[i].dst).y == -1)
10783     {
10784       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10785
10786       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10787
10788       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10789     }
10790   }
10791
10792   // adjust editor palette rows and columns if specified to be dynamic
10793
10794   if (editor.palette.cols == -1)
10795   {
10796     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10797     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10798     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10799
10800     editor.palette.cols = (vp_width - sc_width) / bt_width;
10801
10802     if (editor.palette.x == -1)
10803     {
10804       int palette_width = editor.palette.cols * bt_width + sc_width;
10805
10806       editor.palette.x = (vp_width - palette_width) / 2;
10807     }
10808   }
10809
10810   if (editor.palette.rows == -1)
10811   {
10812     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
10813     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
10814     int tx_height = getFontHeight(FONT_TEXT_2);
10815
10816     editor.palette.rows = (vp_height - tx_height) / bt_height;
10817
10818     if (editor.palette.y == -1)
10819     {
10820       int palette_height = editor.palette.rows * bt_height + tx_height;
10821
10822       editor.palette.y = (vp_height - palette_height) / 2;
10823     }
10824   }
10825 }
10826
10827 static void LoadMenuDesignSettingsFromFilename(char *filename)
10828 {
10829   static struct TitleFadingInfo tfi;
10830   static struct TitleMessageInfo tmi;
10831   static struct TokenInfo title_tokens[] =
10832   {
10833     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
10834     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
10835     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
10836     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
10837     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
10838
10839     { -1,               NULL,                   NULL                    }
10840   };
10841   static struct TokenInfo titlemessage_tokens[] =
10842   {
10843     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
10844     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
10845     { TYPE_INTEGER,     &tmi.width,             ".width"                },
10846     { TYPE_INTEGER,     &tmi.height,            ".height"               },
10847     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
10848     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
10849     { TYPE_INTEGER,     &tmi.align,             ".align"                },
10850     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
10851     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
10852     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
10853     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
10854     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
10855     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
10856     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
10857     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
10858     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
10859     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
10860     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
10861
10862     { -1,               NULL,                   NULL                    }
10863   };
10864   static struct
10865   {
10866     struct TitleFadingInfo *info;
10867     char *text;
10868   }
10869   title_info[] =
10870   {
10871     // initialize first titles from "enter screen" definitions, if defined
10872     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
10873     { &title_first_default,             "menu.enter_screen.TITLE"       },
10874
10875     // initialize title screens from "next screen" definitions, if defined
10876     { &title_initial_default,           "menu.next_screen.TITLE"        },
10877     { &title_default,                   "menu.next_screen.TITLE"        },
10878
10879     { NULL,                             NULL                            }
10880   };
10881   static struct
10882   {
10883     struct TitleMessageInfo *array;
10884     char *text;
10885   }
10886   titlemessage_arrays[] =
10887   {
10888     // initialize first titles from "enter screen" definitions, if defined
10889     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
10890     { titlescreen_first,                "menu.enter_screen.TITLE"       },
10891     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
10892     { titlemessage_first,               "menu.enter_screen.TITLE"       },
10893
10894     // initialize titles from "next screen" definitions, if defined
10895     { titlescreen_initial,              "menu.next_screen.TITLE"        },
10896     { titlescreen,                      "menu.next_screen.TITLE"        },
10897     { titlemessage_initial,             "menu.next_screen.TITLE"        },
10898     { titlemessage,                     "menu.next_screen.TITLE"        },
10899
10900     // overwrite titles with title definitions, if defined
10901     { titlescreen_initial_first,        "[title_initial]"               },
10902     { titlescreen_first,                "[title]"                       },
10903     { titlemessage_initial_first,       "[title_initial]"               },
10904     { titlemessage_first,               "[title]"                       },
10905
10906     { titlescreen_initial,              "[title_initial]"               },
10907     { titlescreen,                      "[title]"                       },
10908     { titlemessage_initial,             "[title_initial]"               },
10909     { titlemessage,                     "[title]"                       },
10910
10911     // overwrite titles with title screen/message definitions, if defined
10912     { titlescreen_initial_first,        "[titlescreen_initial]"         },
10913     { titlescreen_first,                "[titlescreen]"                 },
10914     { titlemessage_initial_first,       "[titlemessage_initial]"        },
10915     { titlemessage_first,               "[titlemessage]"                },
10916
10917     { titlescreen_initial,              "[titlescreen_initial]"         },
10918     { titlescreen,                      "[titlescreen]"                 },
10919     { titlemessage_initial,             "[titlemessage_initial]"        },
10920     { titlemessage,                     "[titlemessage]"                },
10921
10922     { NULL,                             NULL                            }
10923   };
10924   SetupFileHash *setup_file_hash;
10925   int i, j, k;
10926
10927   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10928     return;
10929
10930   // the following initializes hierarchical values from dynamic configuration
10931
10932   // special case: initialize with default values that may be overwritten
10933   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
10934   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10935   {
10936     struct TokenIntPtrInfo menu_config[] =
10937     {
10938       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
10939       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
10940       { "menu.list_size",       &menu.list_size[i]      }
10941     };
10942
10943     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10944     {
10945       char *token = menu_config[j].token;
10946       char *value = getHashEntry(setup_file_hash, token);
10947
10948       if (value != NULL)
10949         *menu_config[j].value = get_integer_from_string(value);
10950     }
10951   }
10952
10953   // special case: initialize with default values that may be overwritten
10954   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
10955   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10956   {
10957     struct TokenIntPtrInfo menu_config[] =
10958     {
10959       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
10960       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
10961       { "menu.list_size.INFO",          &menu.list_size_info[i]         }
10962     };
10963
10964     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10965     {
10966       char *token = menu_config[j].token;
10967       char *value = getHashEntry(setup_file_hash, token);
10968
10969       if (value != NULL)
10970         *menu_config[j].value = get_integer_from_string(value);
10971     }
10972   }
10973
10974   // special case: initialize with default values that may be overwritten
10975   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
10976   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
10977   {
10978     struct TokenIntPtrInfo menu_config[] =
10979     {
10980       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
10981       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
10982     };
10983
10984     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10985     {
10986       char *token = menu_config[j].token;
10987       char *value = getHashEntry(setup_file_hash, token);
10988
10989       if (value != NULL)
10990         *menu_config[j].value = get_integer_from_string(value);
10991     }
10992   }
10993
10994   // special case: initialize with default values that may be overwritten
10995   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
10996   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10997   {
10998     struct TokenIntPtrInfo menu_config[] =
10999     {
11000       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
11001       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
11002       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
11003       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
11004       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
11005       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
11006       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
11007       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
11008       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
11009     };
11010
11011     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11012     {
11013       char *token = menu_config[j].token;
11014       char *value = getHashEntry(setup_file_hash, token);
11015
11016       if (value != NULL)
11017         *menu_config[j].value = get_integer_from_string(value);
11018     }
11019   }
11020
11021   // special case: initialize with default values that may be overwritten
11022   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11023   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11024   {
11025     struct TokenIntPtrInfo menu_config[] =
11026     {
11027       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
11028       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11029       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11030       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
11031       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11032       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11033       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
11034       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
11035       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
11036     };
11037
11038     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11039     {
11040       char *token = menu_config[j].token;
11041       char *value = getHashEntry(setup_file_hash, token);
11042
11043       if (value != NULL)
11044         *menu_config[j].value = get_token_parameter_value(token, value);
11045     }
11046   }
11047
11048   // special case: initialize with default values that may be overwritten
11049   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11050   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11051   {
11052     struct
11053     {
11054       char *token_prefix;
11055       struct RectWithBorder *struct_ptr;
11056     }
11057     vp_struct[] =
11058     {
11059       { "viewport.window",      &viewport.window[i]     },
11060       { "viewport.playfield",   &viewport.playfield[i]  },
11061       { "viewport.door_1",      &viewport.door_1[i]     },
11062       { "viewport.door_2",      &viewport.door_2[i]     }
11063     };
11064
11065     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11066     {
11067       struct TokenIntPtrInfo vp_config[] =
11068       {
11069         { ".x",                 &vp_struct[j].struct_ptr->x             },
11070         { ".y",                 &vp_struct[j].struct_ptr->y             },
11071         { ".width",             &vp_struct[j].struct_ptr->width         },
11072         { ".height",            &vp_struct[j].struct_ptr->height        },
11073         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
11074         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
11075         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
11076         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
11077         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
11078         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
11079         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
11080         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
11081         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
11082         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
11083         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
11084         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
11085         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
11086         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
11087         { ".align",             &vp_struct[j].struct_ptr->align         },
11088         { ".valign",            &vp_struct[j].struct_ptr->valign        }
11089       };
11090
11091       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11092       {
11093         char *token = getStringCat2(vp_struct[j].token_prefix,
11094                                     vp_config[k].token);
11095         char *value = getHashEntry(setup_file_hash, token);
11096
11097         if (value != NULL)
11098           *vp_config[k].value = get_token_parameter_value(token, value);
11099
11100         free(token);
11101       }
11102     }
11103   }
11104
11105   // special case: initialize with default values that may be overwritten
11106   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11107   for (i = 0; title_info[i].info != NULL; i++)
11108   {
11109     struct TitleFadingInfo *info = title_info[i].info;
11110     char *base_token = title_info[i].text;
11111
11112     for (j = 0; title_tokens[j].type != -1; j++)
11113     {
11114       char *token = getStringCat2(base_token, title_tokens[j].text);
11115       char *value = getHashEntry(setup_file_hash, token);
11116
11117       if (value != NULL)
11118       {
11119         int parameter_value = get_token_parameter_value(token, value);
11120
11121         tfi = *info;
11122
11123         *(int *)title_tokens[j].value = (int)parameter_value;
11124
11125         *info = tfi;
11126       }
11127
11128       free(token);
11129     }
11130   }
11131
11132   // special case: initialize with default values that may be overwritten
11133   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11134   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11135   {
11136     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11137     char *base_token = titlemessage_arrays[i].text;
11138
11139     for (j = 0; titlemessage_tokens[j].type != -1; j++)
11140     {
11141       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11142       char *value = getHashEntry(setup_file_hash, token);
11143
11144       if (value != NULL)
11145       {
11146         int parameter_value = get_token_parameter_value(token, value);
11147
11148         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11149         {
11150           tmi = array[k];
11151
11152           if (titlemessage_tokens[j].type == TYPE_INTEGER)
11153             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
11154           else
11155             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11156
11157           array[k] = tmi;
11158         }
11159       }
11160
11161       free(token);
11162     }
11163   }
11164
11165   // special case: check if network and preview player positions are redefined,
11166   // to compare this later against the main menu level preview being redefined
11167   struct TokenIntPtrInfo menu_config_players[] =
11168   {
11169     { "main.network_players.x", &menu.main.network_players.redefined    },
11170     { "main.network_players.y", &menu.main.network_players.redefined    },
11171     { "main.preview_players.x", &menu.main.preview_players.redefined    },
11172     { "main.preview_players.y", &menu.main.preview_players.redefined    },
11173     { "preview.x",              &preview.redefined                      },
11174     { "preview.y",              &preview.redefined                      }
11175   };
11176
11177   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11178     *menu_config_players[i].value = FALSE;
11179
11180   for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11181     if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11182       *menu_config_players[i].value = TRUE;
11183
11184   // read (and overwrite with) values that may be specified in config file
11185   for (i = 0; image_config_vars[i].token != NULL; i++)
11186   {
11187     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11188
11189     // (ignore definitions set to "[DEFAULT]" which are already initialized)
11190     if (value != NULL && !strEqual(value, ARG_DEFAULT))
11191       *image_config_vars[i].value =
11192         get_token_parameter_value(image_config_vars[i].token, value);
11193   }
11194
11195   freeSetupFileHash(setup_file_hash);
11196 }
11197
11198 void LoadMenuDesignSettings(void)
11199 {
11200   char *filename_base = UNDEFINED_FILENAME, *filename_local;
11201
11202   InitMenuDesignSettings_Static();
11203   InitMenuDesignSettings_SpecialPreProcessing();
11204
11205   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11206   {
11207     // first look for special settings configured in level series config
11208     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11209
11210     if (fileExists(filename_base))
11211       LoadMenuDesignSettingsFromFilename(filename_base);
11212   }
11213
11214   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11215
11216   if (filename_local != NULL && !strEqual(filename_base, filename_local))
11217     LoadMenuDesignSettingsFromFilename(filename_local);
11218
11219   InitMenuDesignSettings_SpecialPostProcessing();
11220 }
11221
11222 void LoadMenuDesignSettings_AfterGraphics(void)
11223 {
11224   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11225 }
11226
11227 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11228 {
11229   char *filename = getEditorSetupFilename();
11230   SetupFileList *setup_file_list, *list;
11231   SetupFileHash *element_hash;
11232   int num_unknown_tokens = 0;
11233   int i;
11234
11235   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11236     return;
11237
11238   element_hash = newSetupFileHash();
11239
11240   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11241     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11242
11243   // determined size may be larger than needed (due to unknown elements)
11244   *num_elements = 0;
11245   for (list = setup_file_list; list != NULL; list = list->next)
11246     (*num_elements)++;
11247
11248   // add space for up to 3 more elements for padding that may be needed
11249   *num_elements += 3;
11250
11251   // free memory for old list of elements, if needed
11252   checked_free(*elements);
11253
11254   // allocate memory for new list of elements
11255   *elements = checked_malloc(*num_elements * sizeof(int));
11256
11257   *num_elements = 0;
11258   for (list = setup_file_list; list != NULL; list = list->next)
11259   {
11260     char *value = getHashEntry(element_hash, list->token);
11261
11262     if (value == NULL)          // try to find obsolete token mapping
11263     {
11264       char *mapped_token = get_mapped_token(list->token);
11265
11266       if (mapped_token != NULL)
11267       {
11268         value = getHashEntry(element_hash, mapped_token);
11269
11270         free(mapped_token);
11271       }
11272     }
11273
11274     if (value != NULL)
11275     {
11276       (*elements)[(*num_elements)++] = atoi(value);
11277     }
11278     else
11279     {
11280       if (num_unknown_tokens == 0)
11281       {
11282         Error(ERR_INFO_LINE, "-");
11283         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11284         Error(ERR_INFO, "- config file: '%s'", filename);
11285
11286         num_unknown_tokens++;
11287       }
11288
11289       Error(ERR_INFO, "- token: '%s'", list->token);
11290     }
11291   }
11292
11293   if (num_unknown_tokens > 0)
11294     Error(ERR_INFO_LINE, "-");
11295
11296   while (*num_elements % 4)     // pad with empty elements, if needed
11297     (*elements)[(*num_elements)++] = EL_EMPTY;
11298
11299   freeSetupFileList(setup_file_list);
11300   freeSetupFileHash(element_hash);
11301
11302 #if 0
11303   for (i = 0; i < *num_elements; i++)
11304     printf("editor: element '%s' [%d]\n",
11305            element_info[(*elements)[i]].token_name, (*elements)[i]);
11306 #endif
11307 }
11308
11309 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11310                                                      boolean is_sound)
11311 {
11312   SetupFileHash *setup_file_hash = NULL;
11313   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11314   char *filename_music, *filename_prefix, *filename_info;
11315   struct
11316   {
11317     char *token;
11318     char **value_ptr;
11319   }
11320   token_to_value_ptr[] =
11321   {
11322     { "title_header",   &tmp_music_file_info.title_header       },
11323     { "artist_header",  &tmp_music_file_info.artist_header      },
11324     { "album_header",   &tmp_music_file_info.album_header       },
11325     { "year_header",    &tmp_music_file_info.year_header        },
11326
11327     { "title",          &tmp_music_file_info.title              },
11328     { "artist",         &tmp_music_file_info.artist             },
11329     { "album",          &tmp_music_file_info.album              },
11330     { "year",           &tmp_music_file_info.year               },
11331
11332     { NULL,             NULL                                    },
11333   };
11334   int i;
11335
11336   filename_music = (is_sound ? getCustomSoundFilename(basename) :
11337                     getCustomMusicFilename(basename));
11338
11339   if (filename_music == NULL)
11340     return NULL;
11341
11342   // ---------- try to replace file extension ----------
11343
11344   filename_prefix = getStringCopy(filename_music);
11345   if (strrchr(filename_prefix, '.') != NULL)
11346     *strrchr(filename_prefix, '.') = '\0';
11347   filename_info = getStringCat2(filename_prefix, ".txt");
11348
11349   if (fileExists(filename_info))
11350     setup_file_hash = loadSetupFileHash(filename_info);
11351
11352   free(filename_prefix);
11353   free(filename_info);
11354
11355   if (setup_file_hash == NULL)
11356   {
11357     // ---------- try to add file extension ----------
11358
11359     filename_prefix = getStringCopy(filename_music);
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
11369   if (setup_file_hash == NULL)
11370     return NULL;
11371
11372   // ---------- music file info found ----------
11373
11374   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11375
11376   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11377   {
11378     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11379
11380     *token_to_value_ptr[i].value_ptr =
11381       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11382   }
11383
11384   tmp_music_file_info.basename = getStringCopy(basename);
11385   tmp_music_file_info.music = music;
11386   tmp_music_file_info.is_sound = is_sound;
11387
11388   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11389   *new_music_file_info = tmp_music_file_info;
11390
11391   return new_music_file_info;
11392 }
11393
11394 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11395 {
11396   return get_music_file_info_ext(basename, music, FALSE);
11397 }
11398
11399 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11400 {
11401   return get_music_file_info_ext(basename, sound, TRUE);
11402 }
11403
11404 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11405                                      char *basename, boolean is_sound)
11406 {
11407   for (; list != NULL; list = list->next)
11408     if (list->is_sound == is_sound && strEqual(list->basename, basename))
11409       return TRUE;
11410
11411   return FALSE;
11412 }
11413
11414 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11415 {
11416   return music_info_listed_ext(list, basename, FALSE);
11417 }
11418
11419 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11420 {
11421   return music_info_listed_ext(list, basename, TRUE);
11422 }
11423
11424 void LoadMusicInfo(void)
11425 {
11426   char *music_directory = getCustomMusicDirectory();
11427   int num_music = getMusicListSize();
11428   int num_music_noconf = 0;
11429   int num_sounds = getSoundListSize();
11430   Directory *dir;
11431   DirectoryEntry *dir_entry;
11432   struct FileInfo *music, *sound;
11433   struct MusicFileInfo *next, **new;
11434   int i;
11435
11436   while (music_file_info != NULL)
11437   {
11438     next = music_file_info->next;
11439
11440     checked_free(music_file_info->basename);
11441
11442     checked_free(music_file_info->title_header);
11443     checked_free(music_file_info->artist_header);
11444     checked_free(music_file_info->album_header);
11445     checked_free(music_file_info->year_header);
11446
11447     checked_free(music_file_info->title);
11448     checked_free(music_file_info->artist);
11449     checked_free(music_file_info->album);
11450     checked_free(music_file_info->year);
11451
11452     free(music_file_info);
11453
11454     music_file_info = next;
11455   }
11456
11457   new = &music_file_info;
11458
11459   for (i = 0; i < num_music; i++)
11460   {
11461     music = getMusicListEntry(i);
11462
11463     if (music->filename == NULL)
11464       continue;
11465
11466     if (strEqual(music->filename, UNDEFINED_FILENAME))
11467       continue;
11468
11469     // a configured file may be not recognized as music
11470     if (!FileIsMusic(music->filename))
11471       continue;
11472
11473     if (!music_info_listed(music_file_info, music->filename))
11474     {
11475       *new = get_music_file_info(music->filename, i);
11476
11477       if (*new != NULL)
11478         new = &(*new)->next;
11479     }
11480   }
11481
11482   if ((dir = openDirectory(music_directory)) == NULL)
11483   {
11484     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
11485     return;
11486   }
11487
11488   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
11489   {
11490     char *basename = dir_entry->basename;
11491     boolean music_already_used = FALSE;
11492     int i;
11493
11494     // skip all music files that are configured in music config file
11495     for (i = 0; i < num_music; i++)
11496     {
11497       music = getMusicListEntry(i);
11498
11499       if (music->filename == NULL)
11500         continue;
11501
11502       if (strEqual(basename, music->filename))
11503       {
11504         music_already_used = TRUE;
11505         break;
11506       }
11507     }
11508
11509     if (music_already_used)
11510       continue;
11511
11512     if (!FileIsMusic(dir_entry->filename))
11513       continue;
11514
11515     if (!music_info_listed(music_file_info, basename))
11516     {
11517       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11518
11519       if (*new != NULL)
11520         new = &(*new)->next;
11521     }
11522
11523     num_music_noconf++;
11524   }
11525
11526   closeDirectory(dir);
11527
11528   for (i = 0; i < num_sounds; i++)
11529   {
11530     sound = getSoundListEntry(i);
11531
11532     if (sound->filename == NULL)
11533       continue;
11534
11535     if (strEqual(sound->filename, UNDEFINED_FILENAME))
11536       continue;
11537
11538     // a configured file may be not recognized as sound
11539     if (!FileIsSound(sound->filename))
11540       continue;
11541
11542     if (!sound_info_listed(music_file_info, sound->filename))
11543     {
11544       *new = get_sound_file_info(sound->filename, i);
11545       if (*new != NULL)
11546         new = &(*new)->next;
11547     }
11548   }
11549 }
11550
11551 static void add_helpanim_entry(int element, int action, int direction,
11552                                int delay, int *num_list_entries)
11553 {
11554   struct HelpAnimInfo *new_list_entry;
11555   (*num_list_entries)++;
11556
11557   helpanim_info =
11558     checked_realloc(helpanim_info,
11559                     *num_list_entries * sizeof(struct HelpAnimInfo));
11560   new_list_entry = &helpanim_info[*num_list_entries - 1];
11561
11562   new_list_entry->element = element;
11563   new_list_entry->action = action;
11564   new_list_entry->direction = direction;
11565   new_list_entry->delay = delay;
11566 }
11567
11568 static void print_unknown_token(char *filename, char *token, int token_nr)
11569 {
11570   if (token_nr == 0)
11571   {
11572     Error(ERR_INFO_LINE, "-");
11573     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11574     Error(ERR_INFO, "- config file: '%s'", filename);
11575   }
11576
11577   Error(ERR_INFO, "- token: '%s'", token);
11578 }
11579
11580 static void print_unknown_token_end(int token_nr)
11581 {
11582   if (token_nr > 0)
11583     Error(ERR_INFO_LINE, "-");
11584 }
11585
11586 void LoadHelpAnimInfo(void)
11587 {
11588   char *filename = getHelpAnimFilename();
11589   SetupFileList *setup_file_list = NULL, *list;
11590   SetupFileHash *element_hash, *action_hash, *direction_hash;
11591   int num_list_entries = 0;
11592   int num_unknown_tokens = 0;
11593   int i;
11594
11595   if (fileExists(filename))
11596     setup_file_list = loadSetupFileList(filename);
11597
11598   if (setup_file_list == NULL)
11599   {
11600     // use reliable default values from static configuration
11601     SetupFileList *insert_ptr;
11602
11603     insert_ptr = setup_file_list =
11604       newSetupFileList(helpanim_config[0].token,
11605                        helpanim_config[0].value);
11606
11607     for (i = 1; helpanim_config[i].token; i++)
11608       insert_ptr = addListEntry(insert_ptr,
11609                                 helpanim_config[i].token,
11610                                 helpanim_config[i].value);
11611   }
11612
11613   element_hash   = newSetupFileHash();
11614   action_hash    = newSetupFileHash();
11615   direction_hash = newSetupFileHash();
11616
11617   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11618     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11619
11620   for (i = 0; i < NUM_ACTIONS; i++)
11621     setHashEntry(action_hash, element_action_info[i].suffix,
11622                  i_to_a(element_action_info[i].value));
11623
11624   // do not store direction index (bit) here, but direction value!
11625   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11626     setHashEntry(direction_hash, element_direction_info[i].suffix,
11627                  i_to_a(1 << element_direction_info[i].value));
11628
11629   for (list = setup_file_list; list != NULL; list = list->next)
11630   {
11631     char *element_token, *action_token, *direction_token;
11632     char *element_value, *action_value, *direction_value;
11633     int delay = atoi(list->value);
11634
11635     if (strEqual(list->token, "end"))
11636     {
11637       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11638
11639       continue;
11640     }
11641
11642     /* first try to break element into element/action/direction parts;
11643        if this does not work, also accept combined "element[.act][.dir]"
11644        elements (like "dynamite.active"), which are unique elements */
11645
11646     if (strchr(list->token, '.') == NULL)       // token contains no '.'
11647     {
11648       element_value = getHashEntry(element_hash, list->token);
11649       if (element_value != NULL)        // element found
11650         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11651                            &num_list_entries);
11652       else
11653       {
11654         // no further suffixes found -- this is not an element
11655         print_unknown_token(filename, list->token, num_unknown_tokens++);
11656       }
11657
11658       continue;
11659     }
11660
11661     // token has format "<prefix>.<something>"
11662
11663     action_token = strchr(list->token, '.');    // suffix may be action ...
11664     direction_token = action_token;             // ... or direction
11665
11666     element_token = getStringCopy(list->token);
11667     *strchr(element_token, '.') = '\0';
11668
11669     element_value = getHashEntry(element_hash, element_token);
11670
11671     if (element_value == NULL)          // this is no element
11672     {
11673       element_value = getHashEntry(element_hash, list->token);
11674       if (element_value != NULL)        // combined element found
11675         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11676                            &num_list_entries);
11677       else
11678         print_unknown_token(filename, list->token, num_unknown_tokens++);
11679
11680       free(element_token);
11681
11682       continue;
11683     }
11684
11685     action_value = getHashEntry(action_hash, action_token);
11686
11687     if (action_value != NULL)           // action found
11688     {
11689       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11690                     &num_list_entries);
11691
11692       free(element_token);
11693
11694       continue;
11695     }
11696
11697     direction_value = getHashEntry(direction_hash, direction_token);
11698
11699     if (direction_value != NULL)        // direction found
11700     {
11701       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11702                          &num_list_entries);
11703
11704       free(element_token);
11705
11706       continue;
11707     }
11708
11709     if (strchr(action_token + 1, '.') == NULL)
11710     {
11711       // no further suffixes found -- this is not an action nor direction
11712
11713       element_value = getHashEntry(element_hash, list->token);
11714       if (element_value != NULL)        // combined element found
11715         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11716                            &num_list_entries);
11717       else
11718         print_unknown_token(filename, list->token, num_unknown_tokens++);
11719
11720       free(element_token);
11721
11722       continue;
11723     }
11724
11725     // token has format "<prefix>.<suffix>.<something>"
11726
11727     direction_token = strchr(action_token + 1, '.');
11728
11729     action_token = getStringCopy(action_token);
11730     *strchr(action_token + 1, '.') = '\0';
11731
11732     action_value = getHashEntry(action_hash, action_token);
11733
11734     if (action_value == NULL)           // this is no action
11735     {
11736       element_value = getHashEntry(element_hash, list->token);
11737       if (element_value != NULL)        // combined element found
11738         add_helpanim_entry(atoi(element_value), -1, -1, delay,
11739                            &num_list_entries);
11740       else
11741         print_unknown_token(filename, list->token, num_unknown_tokens++);
11742
11743       free(element_token);
11744       free(action_token);
11745
11746       continue;
11747     }
11748
11749     direction_value = getHashEntry(direction_hash, direction_token);
11750
11751     if (direction_value != NULL)        // direction found
11752     {
11753       add_helpanim_entry(atoi(element_value), atoi(action_value),
11754                          atoi(direction_value), delay, &num_list_entries);
11755
11756       free(element_token);
11757       free(action_token);
11758
11759       continue;
11760     }
11761
11762     // this is no direction
11763
11764     element_value = getHashEntry(element_hash, list->token);
11765     if (element_value != NULL)          // combined element found
11766       add_helpanim_entry(atoi(element_value), -1, -1, delay,
11767                          &num_list_entries);
11768     else
11769       print_unknown_token(filename, list->token, num_unknown_tokens++);
11770
11771     free(element_token);
11772     free(action_token);
11773   }
11774
11775   print_unknown_token_end(num_unknown_tokens);
11776
11777   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11778   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
11779
11780   freeSetupFileList(setup_file_list);
11781   freeSetupFileHash(element_hash);
11782   freeSetupFileHash(action_hash);
11783   freeSetupFileHash(direction_hash);
11784
11785 #if 0
11786   for (i = 0; i < num_list_entries; i++)
11787     printf("::: '%s': %d, %d, %d => %d\n",
11788            EL_NAME(helpanim_info[i].element),
11789            helpanim_info[i].element,
11790            helpanim_info[i].action,
11791            helpanim_info[i].direction,
11792            helpanim_info[i].delay);
11793 #endif
11794 }
11795
11796 void LoadHelpTextInfo(void)
11797 {
11798   char *filename = getHelpTextFilename();
11799   int i;
11800
11801   if (helptext_info != NULL)
11802   {
11803     freeSetupFileHash(helptext_info);
11804     helptext_info = NULL;
11805   }
11806
11807   if (fileExists(filename))
11808     helptext_info = loadSetupFileHash(filename);
11809
11810   if (helptext_info == NULL)
11811   {
11812     // use reliable default values from static configuration
11813     helptext_info = newSetupFileHash();
11814
11815     for (i = 0; helptext_config[i].token; i++)
11816       setHashEntry(helptext_info,
11817                    helptext_config[i].token,
11818                    helptext_config[i].value);
11819   }
11820
11821 #if 0
11822   BEGIN_HASH_ITERATION(helptext_info, itr)
11823   {
11824     printf("::: '%s' => '%s'\n",
11825            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
11826   }
11827   END_HASH_ITERATION(hash, itr)
11828 #endif
11829 }
11830
11831
11832 // ----------------------------------------------------------------------------
11833 // convert levels
11834 // ----------------------------------------------------------------------------
11835
11836 #define MAX_NUM_CONVERT_LEVELS          1000
11837
11838 void ConvertLevels(void)
11839 {
11840   static LevelDirTree *convert_leveldir = NULL;
11841   static int convert_level_nr = -1;
11842   static int num_levels_handled = 0;
11843   static int num_levels_converted = 0;
11844   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
11845   int i;
11846
11847   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
11848                                                global.convert_leveldir);
11849
11850   if (convert_leveldir == NULL)
11851     Error(ERR_EXIT, "no such level identifier: '%s'",
11852           global.convert_leveldir);
11853
11854   leveldir_current = convert_leveldir;
11855
11856   if (global.convert_level_nr != -1)
11857   {
11858     convert_leveldir->first_level = global.convert_level_nr;
11859     convert_leveldir->last_level  = global.convert_level_nr;
11860   }
11861
11862   convert_level_nr = convert_leveldir->first_level;
11863
11864   PrintLine("=", 79);
11865   Print("Converting levels\n");
11866   PrintLine("-", 79);
11867   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
11868   Print("Level series name:       '%s'\n", convert_leveldir->name);
11869   Print("Level series author:     '%s'\n", convert_leveldir->author);
11870   Print("Number of levels:        %d\n",   convert_leveldir->levels);
11871   PrintLine("=", 79);
11872   Print("\n");
11873
11874   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11875     levels_failed[i] = FALSE;
11876
11877   while (convert_level_nr <= convert_leveldir->last_level)
11878   {
11879     char *level_filename;
11880     boolean new_level;
11881
11882     level_nr = convert_level_nr++;
11883
11884     Print("Level %03d: ", level_nr);
11885
11886     LoadLevel(level_nr);
11887     if (level.no_level_file || level.no_valid_file)
11888     {
11889       Print("(no level)\n");
11890       continue;
11891     }
11892
11893     Print("converting level ... ");
11894
11895     level_filename = getDefaultLevelFilename(level_nr);
11896     new_level = !fileExists(level_filename);
11897
11898     if (new_level)
11899     {
11900       SaveLevel(level_nr);
11901
11902       num_levels_converted++;
11903
11904       Print("converted.\n");
11905     }
11906     else
11907     {
11908       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
11909         levels_failed[level_nr] = TRUE;
11910
11911       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
11912     }
11913
11914     num_levels_handled++;
11915   }
11916
11917   Print("\n");
11918   PrintLine("=", 79);
11919   Print("Number of levels handled: %d\n", num_levels_handled);
11920   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
11921          (num_levels_handled ?
11922           num_levels_converted * 100 / num_levels_handled : 0));
11923   PrintLine("-", 79);
11924   Print("Summary (for automatic parsing by scripts):\n");
11925   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
11926          convert_leveldir->identifier, num_levels_converted,
11927          num_levels_handled,
11928          (num_levels_handled ?
11929           num_levels_converted * 100 / num_levels_handled : 0));
11930
11931   if (num_levels_handled != num_levels_converted)
11932   {
11933     Print(", FAILED:");
11934     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11935       if (levels_failed[i])
11936         Print(" %03d", i);
11937   }
11938
11939   Print("\n");
11940   PrintLine("=", 79);
11941
11942   CloseAllAndExit(0);
11943 }
11944
11945
11946 // ----------------------------------------------------------------------------
11947 // create and save images for use in level sketches (raw BMP format)
11948 // ----------------------------------------------------------------------------
11949
11950 void CreateLevelSketchImages(void)
11951 {
11952   Bitmap *bitmap1;
11953   Bitmap *bitmap2;
11954   int i;
11955
11956   InitElementPropertiesGfxElement();
11957
11958   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
11959   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
11960
11961   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11962   {
11963     int element = getMappedElement(i);
11964     char basename1[16];
11965     char basename2[16];
11966     char *filename1;
11967     char *filename2;
11968
11969     sprintf(basename1, "%04d.bmp", i);
11970     sprintf(basename2, "%04ds.bmp", i);
11971
11972     filename1 = getPath2(global.create_images_dir, basename1);
11973     filename2 = getPath2(global.create_images_dir, basename2);
11974
11975     DrawSizedElement(0, 0, element, TILESIZE);
11976     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
11977
11978     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
11979       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
11980
11981     DrawSizedElement(0, 0, element, MINI_TILESIZE);
11982     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
11983
11984     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
11985       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
11986
11987     free(filename1);
11988     free(filename2);
11989
11990     // create corresponding SQL statements (for normal and small images)
11991     if (i < 1000)
11992     {
11993       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
11994       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
11995     }
11996
11997     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
11998     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
11999
12000     // optional: create content for forum level sketch demonstration post
12001     if (options.debug)
12002       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12003   }
12004
12005   FreeBitmap(bitmap1);
12006   FreeBitmap(bitmap2);
12007
12008   if (options.debug)
12009     fprintf(stderr, "\n");
12010
12011   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
12012
12013   CloseAllAndExit(0);
12014 }
12015
12016
12017 // ----------------------------------------------------------------------------
12018 // create and save images for custom and group elements (raw BMP format)
12019 // ----------------------------------------------------------------------------
12020
12021 void CreateCustomElementImages(char *directory)
12022 {
12023   char *src_basename = "RocksCE-template.ilbm";
12024   char *dst_basename = "RocksCE.bmp";
12025   char *src_filename = getPath2(directory, src_basename);
12026   char *dst_filename = getPath2(directory, dst_basename);
12027   Bitmap *src_bitmap;
12028   Bitmap *bitmap;
12029   int yoffset_ce = 0;
12030   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12031   int i;
12032
12033   InitVideoDefaults();
12034
12035   ReCreateBitmap(&backbuffer, video.width, video.height);
12036
12037   src_bitmap = LoadImage(src_filename);
12038
12039   bitmap = CreateBitmap(TILEX * 16 * 2,
12040                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12041                         DEFAULT_DEPTH);
12042
12043   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12044   {
12045     int x = i % 16;
12046     int y = i / 16;
12047     int ii = i + 1;
12048     int j;
12049
12050     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12051                TILEX * x, TILEY * y + yoffset_ce);
12052
12053     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12054                TILEX, TILEY,
12055                TILEX * x + TILEX * 16,
12056                TILEY * y + yoffset_ce);
12057
12058     for (j = 2; j >= 0; j--)
12059     {
12060       int c = ii % 10;
12061
12062       BlitBitmap(src_bitmap, bitmap,
12063                  TILEX + c * 7, 0, 6, 10,
12064                  TILEX * x + 6 + j * 7,
12065                  TILEY * y + 11 + yoffset_ce);
12066
12067       BlitBitmap(src_bitmap, bitmap,
12068                  TILEX + c * 8, TILEY, 6, 10,
12069                  TILEX * 16 + TILEX * x + 6 + j * 8,
12070                  TILEY * y + 10 + yoffset_ce);
12071
12072       ii /= 10;
12073     }
12074   }
12075
12076   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12077   {
12078     int x = i % 16;
12079     int y = i / 16;
12080     int ii = i + 1;
12081     int j;
12082
12083     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12084                TILEX * x, TILEY * y + yoffset_ge);
12085
12086     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12087                TILEX, TILEY,
12088                TILEX * x + TILEX * 16,
12089                TILEY * y + yoffset_ge);
12090
12091     for (j = 1; j >= 0; j--)
12092     {
12093       int c = ii % 10;
12094
12095       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12096                  TILEX * x + 6 + j * 10,
12097                  TILEY * y + 11 + yoffset_ge);
12098
12099       BlitBitmap(src_bitmap, bitmap,
12100                  TILEX + c * 8, TILEY + 12, 6, 10,
12101                  TILEX * 16 + TILEX * x + 10 + j * 8,
12102                  TILEY * y + 10 + yoffset_ge);
12103
12104       ii /= 10;
12105     }
12106   }
12107
12108   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12109     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
12110
12111   FreeBitmap(bitmap);
12112
12113   CloseAllAndExit(0);
12114 }