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