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