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