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