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