fixed copying level from native R'n'D to MM engine
[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       Ur[x][y] =
3921         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
3922 }
3923
3924 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
3925 {
3926   struct LevelInfo_MM *level_mm = level->native_mm_level;
3927   int x, y;
3928
3929   level->file_version = level_mm->file_version;
3930   level->game_version = level_mm->game_version;
3931   level->encoding_16bit_field = level_mm->encoding_16bit_field;
3932
3933   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
3934   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
3935
3936   level->time = level_mm->time;
3937   level->gems_needed = level_mm->kettles_needed;
3938
3939   strcpy(level->name, level_mm->name);
3940   strcpy(level->author, level_mm->author);
3941
3942   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
3943   level->score[SC_KEY]        = level_mm->score[SC_PACMAN];
3944   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
3945
3946   level->amoeba_speed = level_mm->amoeba_speed;
3947
3948   for (x = 0; x < level->fieldx; x++)
3949     for (y = 0; y < level->fieldy; y++)
3950       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
3951
3952   if (level_mm->auto_count_kettles)
3953   {
3954     level->gems_needed = 0;
3955
3956     for (x = 0; x < level->fieldx; x++)
3957       for (y = 0; y < level->fieldy; y++)
3958         if (level->field[x][y] == EL_MM_KETTLE ||
3959             level->field[x][y] == EL_DF_CELL)
3960           level->gems_needed++;
3961   }
3962 }
3963
3964
3965 /* ------------------------------------------------------------------------- */
3966 /* functions for loading DC level                                            */
3967 /* ------------------------------------------------------------------------- */
3968
3969 #define DC_LEVEL_HEADER_SIZE            344
3970
3971 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3972 {
3973   static int last_data_encoded;
3974   static int offset1;
3975   static int offset2;
3976   int diff;
3977   int diff_hi, diff_lo;
3978   int data_hi, data_lo;
3979   unsigned short data_decoded;
3980
3981   if (init)
3982   {
3983     last_data_encoded = 0;
3984     offset1 = -1;
3985     offset2 = 0;
3986
3987     return 0;
3988   }
3989
3990   diff = data_encoded - last_data_encoded;
3991   diff_hi = diff & ~0xff;
3992   diff_lo = diff &  0xff;
3993
3994   offset2 += diff_lo;
3995
3996   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3997   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3998   data_hi = data_hi & 0xff00;
3999
4000   data_decoded = data_hi | data_lo;
4001
4002   last_data_encoded = data_encoded;
4003
4004   offset1 = (offset1 + 1) % 31;
4005   offset2 = offset2 & 0xff;
4006
4007   return data_decoded;
4008 }
4009
4010 int getMappedElement_DC(int element)
4011 {
4012   switch (element)
4013   {
4014     case 0x0000:
4015       element = EL_ROCK;
4016       break;
4017
4018       /* 0x0117 - 0x036e: (?) */
4019       /* EL_DIAMOND */
4020
4021       /* 0x042d - 0x0684: (?) */
4022       /* EL_EMERALD */
4023
4024     case 0x06f1:
4025       element = EL_NUT;
4026       break;
4027
4028     case 0x074c:
4029       element = EL_BOMB;
4030       break;
4031
4032     case 0x07a4:
4033       element = EL_PEARL;
4034       break;
4035
4036     case 0x0823:
4037       element = EL_CRYSTAL;
4038       break;
4039
4040     case 0x0e77:        /* quicksand (boulder) */
4041       element = EL_QUICKSAND_FAST_FULL;
4042       break;
4043
4044     case 0x0e99:        /* slow quicksand (boulder) */
4045       element = EL_QUICKSAND_FULL;
4046       break;
4047
4048     case 0x0ed2:
4049       element = EL_EM_EXIT_OPEN;
4050       break;
4051
4052     case 0x0ee3:
4053       element = EL_EM_EXIT_CLOSED;
4054       break;
4055
4056     case 0x0eeb:
4057       element = EL_EM_STEEL_EXIT_OPEN;
4058       break;
4059
4060     case 0x0efc:
4061       element = EL_EM_STEEL_EXIT_CLOSED;
4062       break;
4063
4064     case 0x0f4f:        /* dynamite (lit 1) */
4065       element = EL_EM_DYNAMITE_ACTIVE;
4066       break;
4067
4068     case 0x0f57:        /* dynamite (lit 2) */
4069       element = EL_EM_DYNAMITE_ACTIVE;
4070       break;
4071
4072     case 0x0f5f:        /* dynamite (lit 3) */
4073       element = EL_EM_DYNAMITE_ACTIVE;
4074       break;
4075
4076     case 0x0f67:        /* dynamite (lit 4) */
4077       element = EL_EM_DYNAMITE_ACTIVE;
4078       break;
4079
4080     case 0x0f81:
4081     case 0x0f82:
4082     case 0x0f83:
4083     case 0x0f84:
4084       element = EL_AMOEBA_WET;
4085       break;
4086
4087     case 0x0f85:
4088       element = EL_AMOEBA_DROP;
4089       break;
4090
4091     case 0x0fb9:
4092       element = EL_DC_MAGIC_WALL;
4093       break;
4094
4095     case 0x0fd0:
4096       element = EL_SPACESHIP_UP;
4097       break;
4098
4099     case 0x0fd9:
4100       element = EL_SPACESHIP_DOWN;
4101       break;
4102
4103     case 0x0ff1:
4104       element = EL_SPACESHIP_LEFT;
4105       break;
4106
4107     case 0x0ff9:
4108       element = EL_SPACESHIP_RIGHT;
4109       break;
4110
4111     case 0x1057:
4112       element = EL_BUG_UP;
4113       break;
4114
4115     case 0x1060:
4116       element = EL_BUG_DOWN;
4117       break;
4118
4119     case 0x1078:
4120       element = EL_BUG_LEFT;
4121       break;
4122
4123     case 0x1080:
4124       element = EL_BUG_RIGHT;
4125       break;
4126
4127     case 0x10de:
4128       element = EL_MOLE_UP;
4129       break;
4130
4131     case 0x10e7:
4132       element = EL_MOLE_DOWN;
4133       break;
4134
4135     case 0x10ff:
4136       element = EL_MOLE_LEFT;
4137       break;
4138
4139     case 0x1107:
4140       element = EL_MOLE_RIGHT;
4141       break;
4142
4143     case 0x11c0:
4144       element = EL_ROBOT;
4145       break;
4146
4147     case 0x13f5:
4148       element = EL_YAMYAM;
4149       break;
4150
4151     case 0x1425:
4152       element = EL_SWITCHGATE_OPEN;
4153       break;
4154
4155     case 0x1426:
4156       element = EL_SWITCHGATE_CLOSED;
4157       break;
4158
4159     case 0x1437:
4160       element = EL_DC_SWITCHGATE_SWITCH_UP;
4161       break;
4162
4163     case 0x143a:
4164       element = EL_TIMEGATE_CLOSED;
4165       break;
4166
4167     case 0x144c:        /* conveyor belt switch (green) */
4168       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4169       break;
4170
4171     case 0x144f:        /* conveyor belt switch (red) */
4172       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4173       break;
4174
4175     case 0x1452:        /* conveyor belt switch (blue) */
4176       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4177       break;
4178
4179     case 0x145b:
4180       element = EL_CONVEYOR_BELT_3_MIDDLE;
4181       break;
4182
4183     case 0x1463:
4184       element = EL_CONVEYOR_BELT_3_LEFT;
4185       break;
4186
4187     case 0x146b:
4188       element = EL_CONVEYOR_BELT_3_RIGHT;
4189       break;
4190
4191     case 0x1473:
4192       element = EL_CONVEYOR_BELT_1_MIDDLE;
4193       break;
4194
4195     case 0x147b:
4196       element = EL_CONVEYOR_BELT_1_LEFT;
4197       break;
4198
4199     case 0x1483:
4200       element = EL_CONVEYOR_BELT_1_RIGHT;
4201       break;
4202
4203     case 0x148b:
4204       element = EL_CONVEYOR_BELT_4_MIDDLE;
4205       break;
4206
4207     case 0x1493:
4208       element = EL_CONVEYOR_BELT_4_LEFT;
4209       break;
4210
4211     case 0x149b:
4212       element = EL_CONVEYOR_BELT_4_RIGHT;
4213       break;
4214
4215     case 0x14ac:
4216       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4217       break;
4218
4219     case 0x14bd:
4220       element = EL_EXPANDABLE_WALL_VERTICAL;
4221       break;
4222
4223     case 0x14c6:
4224       element = EL_EXPANDABLE_WALL_ANY;
4225       break;
4226
4227     case 0x14ce:        /* growing steel wall (left/right) */
4228       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4229       break;
4230
4231     case 0x14df:        /* growing steel wall (up/down) */
4232       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4233       break;
4234
4235     case 0x14e8:        /* growing steel wall (up/down/left/right) */
4236       element = EL_EXPANDABLE_STEELWALL_ANY;
4237       break;
4238
4239     case 0x14e9:
4240       element = EL_SHIELD_DEADLY;
4241       break;
4242
4243     case 0x1501:
4244       element = EL_EXTRA_TIME;
4245       break;
4246
4247     case 0x154f:
4248       element = EL_ACID;
4249       break;
4250
4251     case 0x1577:
4252       element = EL_EMPTY_SPACE;
4253       break;
4254
4255     case 0x1578:        /* quicksand (empty) */
4256       element = EL_QUICKSAND_FAST_EMPTY;
4257       break;
4258
4259     case 0x1579:        /* slow quicksand (empty) */
4260       element = EL_QUICKSAND_EMPTY;
4261       break;
4262
4263       /* 0x157c - 0x158b: */
4264       /* EL_SAND */
4265
4266       /* 0x1590 - 0x159f: */
4267       /* EL_DC_LANDMINE */
4268
4269     case 0x15a0:
4270       element = EL_EM_DYNAMITE;
4271       break;
4272
4273     case 0x15a1:        /* key (red) */
4274       element = EL_EM_KEY_1;
4275       break;
4276
4277     case 0x15a2:        /* key (yellow) */
4278       element = EL_EM_KEY_2;
4279       break;
4280
4281     case 0x15a3:        /* key (blue) */
4282       element = EL_EM_KEY_4;
4283       break;
4284
4285     case 0x15a4:        /* key (green) */
4286       element = EL_EM_KEY_3;
4287       break;
4288
4289     case 0x15a5:        /* key (white) */
4290       element = EL_DC_KEY_WHITE;
4291       break;
4292
4293     case 0x15a6:
4294       element = EL_WALL_SLIPPERY;
4295       break;
4296
4297     case 0x15a7:
4298       element = EL_WALL;
4299       break;
4300
4301     case 0x15a8:        /* wall (not round) */
4302       element = EL_WALL;
4303       break;
4304
4305     case 0x15a9:        /* (blue) */
4306       element = EL_CHAR_A;
4307       break;
4308
4309     case 0x15aa:        /* (blue) */
4310       element = EL_CHAR_B;
4311       break;
4312
4313     case 0x15ab:        /* (blue) */
4314       element = EL_CHAR_C;
4315       break;
4316
4317     case 0x15ac:        /* (blue) */
4318       element = EL_CHAR_D;
4319       break;
4320
4321     case 0x15ad:        /* (blue) */
4322       element = EL_CHAR_E;
4323       break;
4324
4325     case 0x15ae:        /* (blue) */
4326       element = EL_CHAR_F;
4327       break;
4328
4329     case 0x15af:        /* (blue) */
4330       element = EL_CHAR_G;
4331       break;
4332
4333     case 0x15b0:        /* (blue) */
4334       element = EL_CHAR_H;
4335       break;
4336
4337     case 0x15b1:        /* (blue) */
4338       element = EL_CHAR_I;
4339       break;
4340
4341     case 0x15b2:        /* (blue) */
4342       element = EL_CHAR_J;
4343       break;
4344
4345     case 0x15b3:        /* (blue) */
4346       element = EL_CHAR_K;
4347       break;
4348
4349     case 0x15b4:        /* (blue) */
4350       element = EL_CHAR_L;
4351       break;
4352
4353     case 0x15b5:        /* (blue) */
4354       element = EL_CHAR_M;
4355       break;
4356
4357     case 0x15b6:        /* (blue) */
4358       element = EL_CHAR_N;
4359       break;
4360
4361     case 0x15b7:        /* (blue) */
4362       element = EL_CHAR_O;
4363       break;
4364
4365     case 0x15b8:        /* (blue) */
4366       element = EL_CHAR_P;
4367       break;
4368
4369     case 0x15b9:        /* (blue) */
4370       element = EL_CHAR_Q;
4371       break;
4372
4373     case 0x15ba:        /* (blue) */
4374       element = EL_CHAR_R;
4375       break;
4376
4377     case 0x15bb:        /* (blue) */
4378       element = EL_CHAR_S;
4379       break;
4380
4381     case 0x15bc:        /* (blue) */
4382       element = EL_CHAR_T;
4383       break;
4384
4385     case 0x15bd:        /* (blue) */
4386       element = EL_CHAR_U;
4387       break;
4388
4389     case 0x15be:        /* (blue) */
4390       element = EL_CHAR_V;
4391       break;
4392
4393     case 0x15bf:        /* (blue) */
4394       element = EL_CHAR_W;
4395       break;
4396
4397     case 0x15c0:        /* (blue) */
4398       element = EL_CHAR_X;
4399       break;
4400
4401     case 0x15c1:        /* (blue) */
4402       element = EL_CHAR_Y;
4403       break;
4404
4405     case 0x15c2:        /* (blue) */
4406       element = EL_CHAR_Z;
4407       break;
4408
4409     case 0x15c3:        /* (blue) */
4410       element = EL_CHAR_AUMLAUT;
4411       break;
4412
4413     case 0x15c4:        /* (blue) */
4414       element = EL_CHAR_OUMLAUT;
4415       break;
4416
4417     case 0x15c5:        /* (blue) */
4418       element = EL_CHAR_UUMLAUT;
4419       break;
4420
4421     case 0x15c6:        /* (blue) */
4422       element = EL_CHAR_0;
4423       break;
4424
4425     case 0x15c7:        /* (blue) */
4426       element = EL_CHAR_1;
4427       break;
4428
4429     case 0x15c8:        /* (blue) */
4430       element = EL_CHAR_2;
4431       break;
4432
4433     case 0x15c9:        /* (blue) */
4434       element = EL_CHAR_3;
4435       break;
4436
4437     case 0x15ca:        /* (blue) */
4438       element = EL_CHAR_4;
4439       break;
4440
4441     case 0x15cb:        /* (blue) */
4442       element = EL_CHAR_5;
4443       break;
4444
4445     case 0x15cc:        /* (blue) */
4446       element = EL_CHAR_6;
4447       break;
4448
4449     case 0x15cd:        /* (blue) */
4450       element = EL_CHAR_7;
4451       break;
4452
4453     case 0x15ce:        /* (blue) */
4454       element = EL_CHAR_8;
4455       break;
4456
4457     case 0x15cf:        /* (blue) */
4458       element = EL_CHAR_9;
4459       break;
4460
4461     case 0x15d0:        /* (blue) */
4462       element = EL_CHAR_PERIOD;
4463       break;
4464
4465     case 0x15d1:        /* (blue) */
4466       element = EL_CHAR_EXCLAM;
4467       break;
4468
4469     case 0x15d2:        /* (blue) */
4470       element = EL_CHAR_COLON;
4471       break;
4472
4473     case 0x15d3:        /* (blue) */
4474       element = EL_CHAR_LESS;
4475       break;
4476
4477     case 0x15d4:        /* (blue) */
4478       element = EL_CHAR_GREATER;
4479       break;
4480
4481     case 0x15d5:        /* (blue) */
4482       element = EL_CHAR_QUESTION;
4483       break;
4484
4485     case 0x15d6:        /* (blue) */
4486       element = EL_CHAR_COPYRIGHT;
4487       break;
4488
4489     case 0x15d7:        /* (blue) */
4490       element = EL_CHAR_UP;
4491       break;
4492
4493     case 0x15d8:        /* (blue) */
4494       element = EL_CHAR_DOWN;
4495       break;
4496
4497     case 0x15d9:        /* (blue) */
4498       element = EL_CHAR_BUTTON;
4499       break;
4500
4501     case 0x15da:        /* (blue) */
4502       element = EL_CHAR_PLUS;
4503       break;
4504
4505     case 0x15db:        /* (blue) */
4506       element = EL_CHAR_MINUS;
4507       break;
4508
4509     case 0x15dc:        /* (blue) */
4510       element = EL_CHAR_APOSTROPHE;
4511       break;
4512
4513     case 0x15dd:        /* (blue) */
4514       element = EL_CHAR_PARENLEFT;
4515       break;
4516
4517     case 0x15de:        /* (blue) */
4518       element = EL_CHAR_PARENRIGHT;
4519       break;
4520
4521     case 0x15df:        /* (green) */
4522       element = EL_CHAR_A;
4523       break;
4524
4525     case 0x15e0:        /* (green) */
4526       element = EL_CHAR_B;
4527       break;
4528
4529     case 0x15e1:        /* (green) */
4530       element = EL_CHAR_C;
4531       break;
4532
4533     case 0x15e2:        /* (green) */
4534       element = EL_CHAR_D;
4535       break;
4536
4537     case 0x15e3:        /* (green) */
4538       element = EL_CHAR_E;
4539       break;
4540
4541     case 0x15e4:        /* (green) */
4542       element = EL_CHAR_F;
4543       break;
4544
4545     case 0x15e5:        /* (green) */
4546       element = EL_CHAR_G;
4547       break;
4548
4549     case 0x15e6:        /* (green) */
4550       element = EL_CHAR_H;
4551       break;
4552
4553     case 0x15e7:        /* (green) */
4554       element = EL_CHAR_I;
4555       break;
4556
4557     case 0x15e8:        /* (green) */
4558       element = EL_CHAR_J;
4559       break;
4560
4561     case 0x15e9:        /* (green) */
4562       element = EL_CHAR_K;
4563       break;
4564
4565     case 0x15ea:        /* (green) */
4566       element = EL_CHAR_L;
4567       break;
4568
4569     case 0x15eb:        /* (green) */
4570       element = EL_CHAR_M;
4571       break;
4572
4573     case 0x15ec:        /* (green) */
4574       element = EL_CHAR_N;
4575       break;
4576
4577     case 0x15ed:        /* (green) */
4578       element = EL_CHAR_O;
4579       break;
4580
4581     case 0x15ee:        /* (green) */
4582       element = EL_CHAR_P;
4583       break;
4584
4585     case 0x15ef:        /* (green) */
4586       element = EL_CHAR_Q;
4587       break;
4588
4589     case 0x15f0:        /* (green) */
4590       element = EL_CHAR_R;
4591       break;
4592
4593     case 0x15f1:        /* (green) */
4594       element = EL_CHAR_S;
4595       break;
4596
4597     case 0x15f2:        /* (green) */
4598       element = EL_CHAR_T;
4599       break;
4600
4601     case 0x15f3:        /* (green) */
4602       element = EL_CHAR_U;
4603       break;
4604
4605     case 0x15f4:        /* (green) */
4606       element = EL_CHAR_V;
4607       break;
4608
4609     case 0x15f5:        /* (green) */
4610       element = EL_CHAR_W;
4611       break;
4612
4613     case 0x15f6:        /* (green) */
4614       element = EL_CHAR_X;
4615       break;
4616
4617     case 0x15f7:        /* (green) */
4618       element = EL_CHAR_Y;
4619       break;
4620
4621     case 0x15f8:        /* (green) */
4622       element = EL_CHAR_Z;
4623       break;
4624
4625     case 0x15f9:        /* (green) */
4626       element = EL_CHAR_AUMLAUT;
4627       break;
4628
4629     case 0x15fa:        /* (green) */
4630       element = EL_CHAR_OUMLAUT;
4631       break;
4632
4633     case 0x15fb:        /* (green) */
4634       element = EL_CHAR_UUMLAUT;
4635       break;
4636
4637     case 0x15fc:        /* (green) */
4638       element = EL_CHAR_0;
4639       break;
4640
4641     case 0x15fd:        /* (green) */
4642       element = EL_CHAR_1;
4643       break;
4644
4645     case 0x15fe:        /* (green) */
4646       element = EL_CHAR_2;
4647       break;
4648
4649     case 0x15ff:        /* (green) */
4650       element = EL_CHAR_3;
4651       break;
4652
4653     case 0x1600:        /* (green) */
4654       element = EL_CHAR_4;
4655       break;
4656
4657     case 0x1601:        /* (green) */
4658       element = EL_CHAR_5;
4659       break;
4660
4661     case 0x1602:        /* (green) */
4662       element = EL_CHAR_6;
4663       break;
4664
4665     case 0x1603:        /* (green) */
4666       element = EL_CHAR_7;
4667       break;
4668
4669     case 0x1604:        /* (green) */
4670       element = EL_CHAR_8;
4671       break;
4672
4673     case 0x1605:        /* (green) */
4674       element = EL_CHAR_9;
4675       break;
4676
4677     case 0x1606:        /* (green) */
4678       element = EL_CHAR_PERIOD;
4679       break;
4680
4681     case 0x1607:        /* (green) */
4682       element = EL_CHAR_EXCLAM;
4683       break;
4684
4685     case 0x1608:        /* (green) */
4686       element = EL_CHAR_COLON;
4687       break;
4688
4689     case 0x1609:        /* (green) */
4690       element = EL_CHAR_LESS;
4691       break;
4692
4693     case 0x160a:        /* (green) */
4694       element = EL_CHAR_GREATER;
4695       break;
4696
4697     case 0x160b:        /* (green) */
4698       element = EL_CHAR_QUESTION;
4699       break;
4700
4701     case 0x160c:        /* (green) */
4702       element = EL_CHAR_COPYRIGHT;
4703       break;
4704
4705     case 0x160d:        /* (green) */
4706       element = EL_CHAR_UP;
4707       break;
4708
4709     case 0x160e:        /* (green) */
4710       element = EL_CHAR_DOWN;
4711       break;
4712
4713     case 0x160f:        /* (green) */
4714       element = EL_CHAR_BUTTON;
4715       break;
4716
4717     case 0x1610:        /* (green) */
4718       element = EL_CHAR_PLUS;
4719       break;
4720
4721     case 0x1611:        /* (green) */
4722       element = EL_CHAR_MINUS;
4723       break;
4724
4725     case 0x1612:        /* (green) */
4726       element = EL_CHAR_APOSTROPHE;
4727       break;
4728
4729     case 0x1613:        /* (green) */
4730       element = EL_CHAR_PARENLEFT;
4731       break;
4732
4733     case 0x1614:        /* (green) */
4734       element = EL_CHAR_PARENRIGHT;
4735       break;
4736
4737     case 0x1615:        /* (blue steel) */
4738       element = EL_STEEL_CHAR_A;
4739       break;
4740
4741     case 0x1616:        /* (blue steel) */
4742       element = EL_STEEL_CHAR_B;
4743       break;
4744
4745     case 0x1617:        /* (blue steel) */
4746       element = EL_STEEL_CHAR_C;
4747       break;
4748
4749     case 0x1618:        /* (blue steel) */
4750       element = EL_STEEL_CHAR_D;
4751       break;
4752
4753     case 0x1619:        /* (blue steel) */
4754       element = EL_STEEL_CHAR_E;
4755       break;
4756
4757     case 0x161a:        /* (blue steel) */
4758       element = EL_STEEL_CHAR_F;
4759       break;
4760
4761     case 0x161b:        /* (blue steel) */
4762       element = EL_STEEL_CHAR_G;
4763       break;
4764
4765     case 0x161c:        /* (blue steel) */
4766       element = EL_STEEL_CHAR_H;
4767       break;
4768
4769     case 0x161d:        /* (blue steel) */
4770       element = EL_STEEL_CHAR_I;
4771       break;
4772
4773     case 0x161e:        /* (blue steel) */
4774       element = EL_STEEL_CHAR_J;
4775       break;
4776
4777     case 0x161f:        /* (blue steel) */
4778       element = EL_STEEL_CHAR_K;
4779       break;
4780
4781     case 0x1620:        /* (blue steel) */
4782       element = EL_STEEL_CHAR_L;
4783       break;
4784
4785     case 0x1621:        /* (blue steel) */
4786       element = EL_STEEL_CHAR_M;
4787       break;
4788
4789     case 0x1622:        /* (blue steel) */
4790       element = EL_STEEL_CHAR_N;
4791       break;
4792
4793     case 0x1623:        /* (blue steel) */
4794       element = EL_STEEL_CHAR_O;
4795       break;
4796
4797     case 0x1624:        /* (blue steel) */
4798       element = EL_STEEL_CHAR_P;
4799       break;
4800
4801     case 0x1625:        /* (blue steel) */
4802       element = EL_STEEL_CHAR_Q;
4803       break;
4804
4805     case 0x1626:        /* (blue steel) */
4806       element = EL_STEEL_CHAR_R;
4807       break;
4808
4809     case 0x1627:        /* (blue steel) */
4810       element = EL_STEEL_CHAR_S;
4811       break;
4812
4813     case 0x1628:        /* (blue steel) */
4814       element = EL_STEEL_CHAR_T;
4815       break;
4816
4817     case 0x1629:        /* (blue steel) */
4818       element = EL_STEEL_CHAR_U;
4819       break;
4820
4821     case 0x162a:        /* (blue steel) */
4822       element = EL_STEEL_CHAR_V;
4823       break;
4824
4825     case 0x162b:        /* (blue steel) */
4826       element = EL_STEEL_CHAR_W;
4827       break;
4828
4829     case 0x162c:        /* (blue steel) */
4830       element = EL_STEEL_CHAR_X;
4831       break;
4832
4833     case 0x162d:        /* (blue steel) */
4834       element = EL_STEEL_CHAR_Y;
4835       break;
4836
4837     case 0x162e:        /* (blue steel) */
4838       element = EL_STEEL_CHAR_Z;
4839       break;
4840
4841     case 0x162f:        /* (blue steel) */
4842       element = EL_STEEL_CHAR_AUMLAUT;
4843       break;
4844
4845     case 0x1630:        /* (blue steel) */
4846       element = EL_STEEL_CHAR_OUMLAUT;
4847       break;
4848
4849     case 0x1631:        /* (blue steel) */
4850       element = EL_STEEL_CHAR_UUMLAUT;
4851       break;
4852
4853     case 0x1632:        /* (blue steel) */
4854       element = EL_STEEL_CHAR_0;
4855       break;
4856
4857     case 0x1633:        /* (blue steel) */
4858       element = EL_STEEL_CHAR_1;
4859       break;
4860
4861     case 0x1634:        /* (blue steel) */
4862       element = EL_STEEL_CHAR_2;
4863       break;
4864
4865     case 0x1635:        /* (blue steel) */
4866       element = EL_STEEL_CHAR_3;
4867       break;
4868
4869     case 0x1636:        /* (blue steel) */
4870       element = EL_STEEL_CHAR_4;
4871       break;
4872
4873     case 0x1637:        /* (blue steel) */
4874       element = EL_STEEL_CHAR_5;
4875       break;
4876
4877     case 0x1638:        /* (blue steel) */
4878       element = EL_STEEL_CHAR_6;
4879       break;
4880
4881     case 0x1639:        /* (blue steel) */
4882       element = EL_STEEL_CHAR_7;
4883       break;
4884
4885     case 0x163a:        /* (blue steel) */
4886       element = EL_STEEL_CHAR_8;
4887       break;
4888
4889     case 0x163b:        /* (blue steel) */
4890       element = EL_STEEL_CHAR_9;
4891       break;
4892
4893     case 0x163c:        /* (blue steel) */
4894       element = EL_STEEL_CHAR_PERIOD;
4895       break;
4896
4897     case 0x163d:        /* (blue steel) */
4898       element = EL_STEEL_CHAR_EXCLAM;
4899       break;
4900
4901     case 0x163e:        /* (blue steel) */
4902       element = EL_STEEL_CHAR_COLON;
4903       break;
4904
4905     case 0x163f:        /* (blue steel) */
4906       element = EL_STEEL_CHAR_LESS;
4907       break;
4908
4909     case 0x1640:        /* (blue steel) */
4910       element = EL_STEEL_CHAR_GREATER;
4911       break;
4912
4913     case 0x1641:        /* (blue steel) */
4914       element = EL_STEEL_CHAR_QUESTION;
4915       break;
4916
4917     case 0x1642:        /* (blue steel) */
4918       element = EL_STEEL_CHAR_COPYRIGHT;
4919       break;
4920
4921     case 0x1643:        /* (blue steel) */
4922       element = EL_STEEL_CHAR_UP;
4923       break;
4924
4925     case 0x1644:        /* (blue steel) */
4926       element = EL_STEEL_CHAR_DOWN;
4927       break;
4928
4929     case 0x1645:        /* (blue steel) */
4930       element = EL_STEEL_CHAR_BUTTON;
4931       break;
4932
4933     case 0x1646:        /* (blue steel) */
4934       element = EL_STEEL_CHAR_PLUS;
4935       break;
4936
4937     case 0x1647:        /* (blue steel) */
4938       element = EL_STEEL_CHAR_MINUS;
4939       break;
4940
4941     case 0x1648:        /* (blue steel) */
4942       element = EL_STEEL_CHAR_APOSTROPHE;
4943       break;
4944
4945     case 0x1649:        /* (blue steel) */
4946       element = EL_STEEL_CHAR_PARENLEFT;
4947       break;
4948
4949     case 0x164a:        /* (blue steel) */
4950       element = EL_STEEL_CHAR_PARENRIGHT;
4951       break;
4952
4953     case 0x164b:        /* (green steel) */
4954       element = EL_STEEL_CHAR_A;
4955       break;
4956
4957     case 0x164c:        /* (green steel) */
4958       element = EL_STEEL_CHAR_B;
4959       break;
4960
4961     case 0x164d:        /* (green steel) */
4962       element = EL_STEEL_CHAR_C;
4963       break;
4964
4965     case 0x164e:        /* (green steel) */
4966       element = EL_STEEL_CHAR_D;
4967       break;
4968
4969     case 0x164f:        /* (green steel) */
4970       element = EL_STEEL_CHAR_E;
4971       break;
4972
4973     case 0x1650:        /* (green steel) */
4974       element = EL_STEEL_CHAR_F;
4975       break;
4976
4977     case 0x1651:        /* (green steel) */
4978       element = EL_STEEL_CHAR_G;
4979       break;
4980
4981     case 0x1652:        /* (green steel) */
4982       element = EL_STEEL_CHAR_H;
4983       break;
4984
4985     case 0x1653:        /* (green steel) */
4986       element = EL_STEEL_CHAR_I;
4987       break;
4988
4989     case 0x1654:        /* (green steel) */
4990       element = EL_STEEL_CHAR_J;
4991       break;
4992
4993     case 0x1655:        /* (green steel) */
4994       element = EL_STEEL_CHAR_K;
4995       break;
4996
4997     case 0x1656:        /* (green steel) */
4998       element = EL_STEEL_CHAR_L;
4999       break;
5000
5001     case 0x1657:        /* (green steel) */
5002       element = EL_STEEL_CHAR_M;
5003       break;
5004
5005     case 0x1658:        /* (green steel) */
5006       element = EL_STEEL_CHAR_N;
5007       break;
5008
5009     case 0x1659:        /* (green steel) */
5010       element = EL_STEEL_CHAR_O;
5011       break;
5012
5013     case 0x165a:        /* (green steel) */
5014       element = EL_STEEL_CHAR_P;
5015       break;
5016
5017     case 0x165b:        /* (green steel) */
5018       element = EL_STEEL_CHAR_Q;
5019       break;
5020
5021     case 0x165c:        /* (green steel) */
5022       element = EL_STEEL_CHAR_R;
5023       break;
5024
5025     case 0x165d:        /* (green steel) */
5026       element = EL_STEEL_CHAR_S;
5027       break;
5028
5029     case 0x165e:        /* (green steel) */
5030       element = EL_STEEL_CHAR_T;
5031       break;
5032
5033     case 0x165f:        /* (green steel) */
5034       element = EL_STEEL_CHAR_U;
5035       break;
5036
5037     case 0x1660:        /* (green steel) */
5038       element = EL_STEEL_CHAR_V;
5039       break;
5040
5041     case 0x1661:        /* (green steel) */
5042       element = EL_STEEL_CHAR_W;
5043       break;
5044
5045     case 0x1662:        /* (green steel) */
5046       element = EL_STEEL_CHAR_X;
5047       break;
5048
5049     case 0x1663:        /* (green steel) */
5050       element = EL_STEEL_CHAR_Y;
5051       break;
5052
5053     case 0x1664:        /* (green steel) */
5054       element = EL_STEEL_CHAR_Z;
5055       break;
5056
5057     case 0x1665:        /* (green steel) */
5058       element = EL_STEEL_CHAR_AUMLAUT;
5059       break;
5060
5061     case 0x1666:        /* (green steel) */
5062       element = EL_STEEL_CHAR_OUMLAUT;
5063       break;
5064
5065     case 0x1667:        /* (green steel) */
5066       element = EL_STEEL_CHAR_UUMLAUT;
5067       break;
5068
5069     case 0x1668:        /* (green steel) */
5070       element = EL_STEEL_CHAR_0;
5071       break;
5072
5073     case 0x1669:        /* (green steel) */
5074       element = EL_STEEL_CHAR_1;
5075       break;
5076
5077     case 0x166a:        /* (green steel) */
5078       element = EL_STEEL_CHAR_2;
5079       break;
5080
5081     case 0x166b:        /* (green steel) */
5082       element = EL_STEEL_CHAR_3;
5083       break;
5084
5085     case 0x166c:        /* (green steel) */
5086       element = EL_STEEL_CHAR_4;
5087       break;
5088
5089     case 0x166d:        /* (green steel) */
5090       element = EL_STEEL_CHAR_5;
5091       break;
5092
5093     case 0x166e:        /* (green steel) */
5094       element = EL_STEEL_CHAR_6;
5095       break;
5096
5097     case 0x166f:        /* (green steel) */
5098       element = EL_STEEL_CHAR_7;
5099       break;
5100
5101     case 0x1670:        /* (green steel) */
5102       element = EL_STEEL_CHAR_8;
5103       break;
5104
5105     case 0x1671:        /* (green steel) */
5106       element = EL_STEEL_CHAR_9;
5107       break;
5108
5109     case 0x1672:        /* (green steel) */
5110       element = EL_STEEL_CHAR_PERIOD;
5111       break;
5112
5113     case 0x1673:        /* (green steel) */
5114       element = EL_STEEL_CHAR_EXCLAM;
5115       break;
5116
5117     case 0x1674:        /* (green steel) */
5118       element = EL_STEEL_CHAR_COLON;
5119       break;
5120
5121     case 0x1675:        /* (green steel) */
5122       element = EL_STEEL_CHAR_LESS;
5123       break;
5124
5125     case 0x1676:        /* (green steel) */
5126       element = EL_STEEL_CHAR_GREATER;
5127       break;
5128
5129     case 0x1677:        /* (green steel) */
5130       element = EL_STEEL_CHAR_QUESTION;
5131       break;
5132
5133     case 0x1678:        /* (green steel) */
5134       element = EL_STEEL_CHAR_COPYRIGHT;
5135       break;
5136
5137     case 0x1679:        /* (green steel) */
5138       element = EL_STEEL_CHAR_UP;
5139       break;
5140
5141     case 0x167a:        /* (green steel) */
5142       element = EL_STEEL_CHAR_DOWN;
5143       break;
5144
5145     case 0x167b:        /* (green steel) */
5146       element = EL_STEEL_CHAR_BUTTON;
5147       break;
5148
5149     case 0x167c:        /* (green steel) */
5150       element = EL_STEEL_CHAR_PLUS;
5151       break;
5152
5153     case 0x167d:        /* (green steel) */
5154       element = EL_STEEL_CHAR_MINUS;
5155       break;
5156
5157     case 0x167e:        /* (green steel) */
5158       element = EL_STEEL_CHAR_APOSTROPHE;
5159       break;
5160
5161     case 0x167f:        /* (green steel) */
5162       element = EL_STEEL_CHAR_PARENLEFT;
5163       break;
5164
5165     case 0x1680:        /* (green steel) */
5166       element = EL_STEEL_CHAR_PARENRIGHT;
5167       break;
5168
5169     case 0x1681:        /* gate (red) */
5170       element = EL_EM_GATE_1;
5171       break;
5172
5173     case 0x1682:        /* secret gate (red) */
5174       element = EL_GATE_1_GRAY;
5175       break;
5176
5177     case 0x1683:        /* gate (yellow) */
5178       element = EL_EM_GATE_2;
5179       break;
5180
5181     case 0x1684:        /* secret gate (yellow) */
5182       element = EL_GATE_2_GRAY;
5183       break;
5184
5185     case 0x1685:        /* gate (blue) */
5186       element = EL_EM_GATE_4;
5187       break;
5188
5189     case 0x1686:        /* secret gate (blue) */
5190       element = EL_GATE_4_GRAY;
5191       break;
5192
5193     case 0x1687:        /* gate (green) */
5194       element = EL_EM_GATE_3;
5195       break;
5196
5197     case 0x1688:        /* secret gate (green) */
5198       element = EL_GATE_3_GRAY;
5199       break;
5200
5201     case 0x1689:        /* gate (white) */
5202       element = EL_DC_GATE_WHITE;
5203       break;
5204
5205     case 0x168a:        /* secret gate (white) */
5206       element = EL_DC_GATE_WHITE_GRAY;
5207       break;
5208
5209     case 0x168b:        /* secret gate (no key) */
5210       element = EL_DC_GATE_FAKE_GRAY;
5211       break;
5212
5213     case 0x168c:
5214       element = EL_ROBOT_WHEEL;
5215       break;
5216
5217     case 0x168d:
5218       element = EL_DC_TIMEGATE_SWITCH;
5219       break;
5220
5221     case 0x168e:
5222       element = EL_ACID_POOL_BOTTOM;
5223       break;
5224
5225     case 0x168f:
5226       element = EL_ACID_POOL_TOPLEFT;
5227       break;
5228
5229     case 0x1690:
5230       element = EL_ACID_POOL_TOPRIGHT;
5231       break;
5232
5233     case 0x1691:
5234       element = EL_ACID_POOL_BOTTOMLEFT;
5235       break;
5236
5237     case 0x1692:
5238       element = EL_ACID_POOL_BOTTOMRIGHT;
5239       break;
5240
5241     case 0x1693:
5242       element = EL_STEELWALL;
5243       break;
5244
5245     case 0x1694:
5246       element = EL_STEELWALL_SLIPPERY;
5247       break;
5248
5249     case 0x1695:        /* steel wall (not round) */
5250       element = EL_STEELWALL;
5251       break;
5252
5253     case 0x1696:        /* steel wall (left) */
5254       element = EL_DC_STEELWALL_1_LEFT;
5255       break;
5256
5257     case 0x1697:        /* steel wall (bottom) */
5258       element = EL_DC_STEELWALL_1_BOTTOM;
5259       break;
5260
5261     case 0x1698:        /* steel wall (right) */
5262       element = EL_DC_STEELWALL_1_RIGHT;
5263       break;
5264
5265     case 0x1699:        /* steel wall (top) */
5266       element = EL_DC_STEELWALL_1_TOP;
5267       break;
5268
5269     case 0x169a:        /* steel wall (left/bottom) */
5270       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5271       break;
5272
5273     case 0x169b:        /* steel wall (right/bottom) */
5274       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5275       break;
5276
5277     case 0x169c:        /* steel wall (right/top) */
5278       element = EL_DC_STEELWALL_1_TOPRIGHT;
5279       break;
5280
5281     case 0x169d:        /* steel wall (left/top) */
5282       element = EL_DC_STEELWALL_1_TOPLEFT;
5283       break;
5284
5285     case 0x169e:        /* steel wall (right/bottom small) */
5286       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5287       break;
5288
5289     case 0x169f:        /* steel wall (left/bottom small) */
5290       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5291       break;
5292
5293     case 0x16a0:        /* steel wall (right/top small) */
5294       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5295       break;
5296
5297     case 0x16a1:        /* steel wall (left/top small) */
5298       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5299       break;
5300
5301     case 0x16a2:        /* steel wall (left/right) */
5302       element = EL_DC_STEELWALL_1_VERTICAL;
5303       break;
5304
5305     case 0x16a3:        /* steel wall (top/bottom) */
5306       element = EL_DC_STEELWALL_1_HORIZONTAL;
5307       break;
5308
5309     case 0x16a4:        /* steel wall 2 (left end) */
5310       element = EL_DC_STEELWALL_2_LEFT;
5311       break;
5312
5313     case 0x16a5:        /* steel wall 2 (right end) */
5314       element = EL_DC_STEELWALL_2_RIGHT;
5315       break;
5316
5317     case 0x16a6:        /* steel wall 2 (top end) */
5318       element = EL_DC_STEELWALL_2_TOP;
5319       break;
5320
5321     case 0x16a7:        /* steel wall 2 (bottom end) */
5322       element = EL_DC_STEELWALL_2_BOTTOM;
5323       break;
5324
5325     case 0x16a8:        /* steel wall 2 (left/right) */
5326       element = EL_DC_STEELWALL_2_HORIZONTAL;
5327       break;
5328
5329     case 0x16a9:        /* steel wall 2 (up/down) */
5330       element = EL_DC_STEELWALL_2_VERTICAL;
5331       break;
5332
5333     case 0x16aa:        /* steel wall 2 (mid) */
5334       element = EL_DC_STEELWALL_2_MIDDLE;
5335       break;
5336
5337     case 0x16ab:
5338       element = EL_SIGN_EXCLAMATION;
5339       break;
5340
5341     case 0x16ac:
5342       element = EL_SIGN_RADIOACTIVITY;
5343       break;
5344
5345     case 0x16ad:
5346       element = EL_SIGN_STOP;
5347       break;
5348
5349     case 0x16ae:
5350       element = EL_SIGN_WHEELCHAIR;
5351       break;
5352
5353     case 0x16af:
5354       element = EL_SIGN_PARKING;
5355       break;
5356
5357     case 0x16b0:
5358       element = EL_SIGN_NO_ENTRY;
5359       break;
5360
5361     case 0x16b1:
5362       element = EL_SIGN_HEART;
5363       break;
5364
5365     case 0x16b2:
5366       element = EL_SIGN_GIVE_WAY;
5367       break;
5368
5369     case 0x16b3:
5370       element = EL_SIGN_ENTRY_FORBIDDEN;
5371       break;
5372
5373     case 0x16b4:
5374       element = EL_SIGN_EMERGENCY_EXIT;
5375       break;
5376
5377     case 0x16b5:
5378       element = EL_SIGN_YIN_YANG;
5379       break;
5380
5381     case 0x16b6:
5382       element = EL_WALL_EMERALD;
5383       break;
5384
5385     case 0x16b7:
5386       element = EL_WALL_DIAMOND;
5387       break;
5388
5389     case 0x16b8:
5390       element = EL_WALL_PEARL;
5391       break;
5392
5393     case 0x16b9:
5394       element = EL_WALL_CRYSTAL;
5395       break;
5396
5397     case 0x16ba:
5398       element = EL_INVISIBLE_WALL;
5399       break;
5400
5401     case 0x16bb:
5402       element = EL_INVISIBLE_STEELWALL;
5403       break;
5404
5405       /* 0x16bc - 0x16cb: */
5406       /* EL_INVISIBLE_SAND */
5407
5408     case 0x16cc:
5409       element = EL_LIGHT_SWITCH;
5410       break;
5411
5412     case 0x16cd:
5413       element = EL_ENVELOPE_1;
5414       break;
5415
5416     default:
5417       if (element >= 0x0117 && element <= 0x036e)       /* (?) */
5418         element = EL_DIAMOND;
5419       else if (element >= 0x042d && element <= 0x0684)  /* (?) */
5420         element = EL_EMERALD;
5421       else if (element >= 0x157c && element <= 0x158b)
5422         element = EL_SAND;
5423       else if (element >= 0x1590 && element <= 0x159f)
5424         element = EL_DC_LANDMINE;
5425       else if (element >= 0x16bc && element <= 0x16cb)
5426         element = EL_INVISIBLE_SAND;
5427       else
5428       {
5429         Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5430         element = EL_UNKNOWN;
5431       }
5432       break;
5433   }
5434
5435   return getMappedElement(element);
5436 }
5437
5438 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5439                                        int nr)
5440 {
5441   byte header[DC_LEVEL_HEADER_SIZE];
5442   int envelope_size;
5443   int envelope_header_pos = 62;
5444   int envelope_content_pos = 94;
5445   int level_name_pos = 251;
5446   int level_author_pos = 292;
5447   int envelope_header_len;
5448   int envelope_content_len;
5449   int level_name_len;
5450   int level_author_len;
5451   int fieldx, fieldy;
5452   int num_yamyam_contents;
5453   int i, x, y;
5454
5455   getDecodedWord_DC(0, TRUE);           /* initialize DC2 decoding engine */
5456
5457   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5458   {
5459     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5460
5461     header[i * 2 + 0] = header_word >> 8;
5462     header[i * 2 + 1] = header_word & 0xff;
5463   }
5464
5465   /* read some values from level header to check level decoding integrity */
5466   fieldx = header[6] | (header[7] << 8);
5467   fieldy = header[8] | (header[9] << 8);
5468   num_yamyam_contents = header[60] | (header[61] << 8);
5469
5470   /* do some simple sanity checks to ensure that level was correctly decoded */
5471   if (fieldx < 1 || fieldx > 256 ||
5472       fieldy < 1 || fieldy > 256 ||
5473       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5474   {
5475     level->no_valid_file = TRUE;
5476
5477     Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5478
5479     return;
5480   }
5481
5482   /* maximum envelope header size is 31 bytes */
5483   envelope_header_len   = header[envelope_header_pos];
5484   /* maximum envelope content size is 110 (156?) bytes */
5485   envelope_content_len  = header[envelope_content_pos];
5486
5487   /* maximum level title size is 40 bytes */
5488   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5489   /* maximum level author size is 30 (51?) bytes */
5490   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5491
5492   envelope_size = 0;
5493
5494   for (i = 0; i < envelope_header_len; i++)
5495     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5496       level->envelope[0].text[envelope_size++] =
5497         header[envelope_header_pos + 1 + i];
5498
5499   if (envelope_header_len > 0 && envelope_content_len > 0)
5500   {
5501     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5502       level->envelope[0].text[envelope_size++] = '\n';
5503     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5504       level->envelope[0].text[envelope_size++] = '\n';
5505   }
5506
5507   for (i = 0; i < envelope_content_len; i++)
5508     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5509       level->envelope[0].text[envelope_size++] =
5510         header[envelope_content_pos + 1 + i];
5511
5512   level->envelope[0].text[envelope_size] = '\0';
5513
5514   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5515   level->envelope[0].ysize = 10;
5516   level->envelope[0].autowrap = TRUE;
5517   level->envelope[0].centered = TRUE;
5518
5519   for (i = 0; i < level_name_len; i++)
5520     level->name[i] = header[level_name_pos + 1 + i];
5521   level->name[level_name_len] = '\0';
5522
5523   for (i = 0; i < level_author_len; i++)
5524     level->author[i] = header[level_author_pos + 1 + i];
5525   level->author[level_author_len] = '\0';
5526
5527   num_yamyam_contents = header[60] | (header[61] << 8);
5528   level->num_yamyam_contents =
5529     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5530
5531   for (i = 0; i < num_yamyam_contents; i++)
5532   {
5533     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5534     {
5535       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5536       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5537
5538       if (i < MAX_ELEMENT_CONTENTS)
5539         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5540     }
5541   }
5542
5543   fieldx = header[6] | (header[7] << 8);
5544   fieldy = header[8] | (header[9] << 8);
5545   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5546   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5547
5548   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5549   {
5550     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5551     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5552
5553     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5554       level->field[x][y] = getMappedElement_DC(element_dc);
5555   }
5556
5557   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5558   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5559   level->field[x][y] = EL_PLAYER_1;
5560
5561   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5562   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5563   level->field[x][y] = EL_PLAYER_2;
5564
5565   level->gems_needed            = header[18] | (header[19] << 8);
5566
5567   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
5568   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
5569   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
5570   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
5571   level->score[SC_NUT]          = header[28] | (header[29] << 8);
5572   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
5573   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
5574   level->score[SC_BUG]          = header[34] | (header[35] << 8);
5575   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
5576   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
5577   level->score[SC_KEY]          = header[40] | (header[41] << 8);
5578   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
5579
5580   level->time                   = header[44] | (header[45] << 8);
5581
5582   level->amoeba_speed           = header[46] | (header[47] << 8);
5583   level->time_light             = header[48] | (header[49] << 8);
5584   level->time_timegate          = header[50] | (header[51] << 8);
5585   level->time_wheel             = header[52] | (header[53] << 8);
5586   level->time_magic_wall        = header[54] | (header[55] << 8);
5587   level->extra_time             = header[56] | (header[57] << 8);
5588   level->shield_normal_time     = header[58] | (header[59] << 8);
5589
5590   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5591      can slip down from flat walls, like normal walls and steel walls */
5592   level->em_slippery_gems = TRUE;
5593 }
5594
5595 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5596                                      struct LevelFileInfo *level_file_info,
5597                                      boolean level_info_only)
5598 {
5599   char *filename = level_file_info->filename;
5600   File *file;
5601   int num_magic_bytes = 8;
5602   char magic_bytes[num_magic_bytes + 1];
5603   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5604
5605   if (!(file = openFile(filename, MODE_READ)))
5606   {
5607     level->no_valid_file = TRUE;
5608
5609     if (!level_info_only)
5610       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5611
5612     return;
5613   }
5614
5615   // fseek(file, 0x0000, SEEK_SET);
5616
5617   if (level_file_info->packed)
5618   {
5619     /* read "magic bytes" from start of file */
5620     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5621       magic_bytes[0] = '\0';
5622
5623     /* check "magic bytes" for correct file format */
5624     if (!strPrefix(magic_bytes, "DC2"))
5625     {
5626       level->no_valid_file = TRUE;
5627
5628       Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5629             filename);
5630
5631       return;
5632     }
5633
5634     if (strPrefix(magic_bytes, "DC2Win95") ||
5635         strPrefix(magic_bytes, "DC2Win98"))
5636     {
5637       int position_first_level = 0x00fa;
5638       int extra_bytes = 4;
5639       int skip_bytes;
5640
5641       /* advance file stream to first level inside the level package */
5642       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5643
5644       /* each block of level data is followed by block of non-level data */
5645       num_levels_to_skip *= 2;
5646
5647       /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5648       while (num_levels_to_skip >= 0)
5649       {
5650         /* advance file stream to next level inside the level package */
5651         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5652         {
5653           level->no_valid_file = TRUE;
5654
5655           Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5656                 filename);
5657
5658           return;
5659         }
5660
5661         /* skip apparently unused extra bytes following each level */
5662         ReadUnusedBytesFromFile(file, extra_bytes);
5663
5664         /* read size of next level in level package */
5665         skip_bytes = getFile32BitLE(file);
5666
5667         num_levels_to_skip--;
5668       }
5669     }
5670     else
5671     {
5672       level->no_valid_file = TRUE;
5673
5674       Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5675             filename);
5676
5677       return;
5678     }
5679   }
5680
5681   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5682
5683   closeFile(file);
5684 }
5685
5686
5687 /* ------------------------------------------------------------------------- */
5688 /* functions for loading SB level                                            */
5689 /* ------------------------------------------------------------------------- */
5690
5691 int getMappedElement_SB(int element_ascii, boolean use_ces)
5692 {
5693   static struct
5694   {
5695     int ascii;
5696     int sb;
5697     int ce;
5698   }
5699   sb_element_mapping[] =
5700   {
5701     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  /* floor (space) */
5702     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  /* wall */
5703     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  /* player */
5704     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  /* box */
5705     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  /* goal square */
5706     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  /* box on goal square */
5707     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  /* player on goal square */
5708     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  /* floor beyond border */
5709
5710     { 0,   -1,                      -1          },
5711   };
5712
5713   int i;
5714
5715   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5716     if (element_ascii == sb_element_mapping[i].ascii)
5717       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5718
5719   return EL_UNDEFINED;
5720 }
5721
5722 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5723                                      struct LevelFileInfo *level_file_info,
5724                                      boolean level_info_only)
5725 {
5726   char *filename = level_file_info->filename;
5727   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5728   char last_comment[MAX_LINE_LEN];
5729   char level_name[MAX_LINE_LEN];
5730   char *line_ptr;
5731   File *file;
5732   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5733   boolean read_continued_line = FALSE;
5734   boolean reading_playfield = FALSE;
5735   boolean got_valid_playfield_line = FALSE;
5736   boolean invalid_playfield_char = FALSE;
5737   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5738   int file_level_nr = 0;
5739   int line_nr = 0;
5740   int x = 0, y = 0;             /* initialized to make compilers happy */
5741
5742   last_comment[0] = '\0';
5743   level_name[0] = '\0';
5744
5745   if (!(file = openFile(filename, MODE_READ)))
5746   {
5747     level->no_valid_file = TRUE;
5748
5749     if (!level_info_only)
5750       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5751
5752     return;
5753   }
5754
5755   while (!checkEndOfFile(file))
5756   {
5757     /* level successfully read, but next level may follow here */
5758     if (!got_valid_playfield_line && reading_playfield)
5759     {
5760       /* read playfield from single level file -- skip remaining file */
5761       if (!level_file_info->packed)
5762         break;
5763
5764       if (file_level_nr >= num_levels_to_skip)
5765         break;
5766
5767       file_level_nr++;
5768
5769       last_comment[0] = '\0';
5770       level_name[0] = '\0';
5771
5772       reading_playfield = FALSE;
5773     }
5774
5775     got_valid_playfield_line = FALSE;
5776
5777     /* read next line of input file */
5778     if (!getStringFromFile(file, line, MAX_LINE_LEN))
5779       break;
5780
5781     /* check if line was completely read and is terminated by line break */
5782     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5783       line_nr++;
5784
5785     /* cut trailing line break (this can be newline and/or carriage return) */
5786     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5787       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5788         *line_ptr = '\0';
5789
5790     /* copy raw input line for later use (mainly debugging output) */
5791     strcpy(line_raw, line);
5792
5793     if (read_continued_line)
5794     {
5795       /* append new line to existing line, if there is enough space */
5796       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5797         strcat(previous_line, line_ptr);
5798
5799       strcpy(line, previous_line);      /* copy storage buffer to line */
5800
5801       read_continued_line = FALSE;
5802     }
5803
5804     /* if the last character is '\', continue at next line */
5805     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5806     {
5807       line[strlen(line) - 1] = '\0';    /* cut off trailing backslash */
5808       strcpy(previous_line, line);      /* copy line to storage buffer */
5809
5810       read_continued_line = TRUE;
5811
5812       continue;
5813     }
5814
5815     /* skip empty lines */
5816     if (line[0] == '\0')
5817       continue;
5818
5819     /* extract comment text from comment line */
5820     if (line[0] == ';')
5821     {
5822       for (line_ptr = line; *line_ptr; line_ptr++)
5823         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5824           break;
5825
5826       strcpy(last_comment, line_ptr);
5827
5828       continue;
5829     }
5830
5831     /* extract level title text from line containing level title */
5832     if (line[0] == '\'')
5833     {
5834       strcpy(level_name, &line[1]);
5835
5836       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5837         level_name[strlen(level_name) - 1] = '\0';
5838
5839       continue;
5840     }
5841
5842     /* skip lines containing only spaces (or empty lines) */
5843     for (line_ptr = line; *line_ptr; line_ptr++)
5844       if (*line_ptr != ' ')
5845         break;
5846     if (*line_ptr == '\0')
5847       continue;
5848
5849     /* at this point, we have found a line containing part of a playfield */
5850
5851     got_valid_playfield_line = TRUE;
5852
5853     if (!reading_playfield)
5854     {
5855       reading_playfield = TRUE;
5856       invalid_playfield_char = FALSE;
5857
5858       for (x = 0; x < MAX_LEV_FIELDX; x++)
5859         for (y = 0; y < MAX_LEV_FIELDY; y++)
5860           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5861
5862       level->fieldx = 0;
5863       level->fieldy = 0;
5864
5865       /* start with topmost tile row */
5866       y = 0;
5867     }
5868
5869     /* skip playfield line if larger row than allowed */
5870     if (y >= MAX_LEV_FIELDY)
5871       continue;
5872
5873     /* start with leftmost tile column */
5874     x = 0;
5875
5876     /* read playfield elements from line */
5877     for (line_ptr = line; *line_ptr; line_ptr++)
5878     {
5879       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5880
5881       /* stop parsing playfield line if larger column than allowed */
5882       if (x >= MAX_LEV_FIELDX)
5883         break;
5884
5885       if (mapped_sb_element == EL_UNDEFINED)
5886       {
5887         invalid_playfield_char = TRUE;
5888
5889         break;
5890       }
5891
5892       level->field[x][y] = mapped_sb_element;
5893
5894       /* continue with next tile column */
5895       x++;
5896
5897       level->fieldx = MAX(x, level->fieldx);
5898     }
5899
5900     if (invalid_playfield_char)
5901     {
5902       /* if first playfield line, treat invalid lines as comment lines */
5903       if (y == 0)
5904         reading_playfield = FALSE;
5905
5906       continue;
5907     }
5908
5909     /* continue with next tile row */
5910     y++;
5911   }
5912
5913   closeFile(file);
5914
5915   level->fieldy = y;
5916
5917   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5918   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5919
5920   if (!reading_playfield)
5921   {
5922     level->no_valid_file = TRUE;
5923
5924     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5925
5926     return;
5927   }
5928
5929   if (*level_name != '\0')
5930   {
5931     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5932     level->name[MAX_LEVEL_NAME_LEN] = '\0';
5933   }
5934   else if (*last_comment != '\0')
5935   {
5936     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5937     level->name[MAX_LEVEL_NAME_LEN] = '\0';
5938   }
5939   else
5940   {
5941     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5942   }
5943
5944   /* set all empty fields beyond the border walls to invisible steel wall */
5945   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5946   {
5947     if ((x == 0 || x == level->fieldx - 1 ||
5948          y == 0 || y == level->fieldy - 1) &&
5949         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5950       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5951                      level->field, level->fieldx, level->fieldy);
5952   }
5953
5954   /* set special level settings for Sokoban levels */
5955
5956   level->time = 0;
5957   level->use_step_counter = TRUE;
5958
5959   if (load_xsb_to_ces)
5960   {
5961     /* special global settings can now be set in level template */
5962
5963     level->use_custom_template = TRUE;
5964   }
5965 }
5966
5967
5968 /* ------------------------------------------------------------------------- */
5969 /* functions for handling native levels                                      */
5970 /* ------------------------------------------------------------------------- */
5971
5972 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5973                                      struct LevelFileInfo *level_file_info,
5974                                      boolean level_info_only)
5975 {
5976   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5977     level->no_valid_file = TRUE;
5978 }
5979
5980 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5981                                      struct LevelFileInfo *level_file_info,
5982                                      boolean level_info_only)
5983 {
5984   int pos = 0;
5985
5986   /* determine position of requested level inside level package */
5987   if (level_file_info->packed)
5988     pos = level_file_info->nr - leveldir_current->first_level;
5989
5990   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5991     level->no_valid_file = TRUE;
5992 }
5993
5994 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
5995                                      struct LevelFileInfo *level_file_info,
5996                                      boolean level_info_only)
5997 {
5998   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
5999     level->no_valid_file = TRUE;
6000 }
6001
6002 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6003 {
6004   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6005     CopyNativeLevel_RND_to_EM(level);
6006   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6007     CopyNativeLevel_RND_to_SP(level);
6008   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6009     CopyNativeLevel_RND_to_MM(level);
6010 }
6011
6012 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6013 {
6014   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6015     CopyNativeLevel_EM_to_RND(level);
6016   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6017     CopyNativeLevel_SP_to_RND(level);
6018   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6019     CopyNativeLevel_MM_to_RND(level);
6020 }
6021
6022 void SaveNativeLevel(struct LevelInfo *level)
6023 {
6024   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6025   {
6026     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6027     char *filename = getLevelFilenameFromBasename(basename);
6028
6029     CopyNativeLevel_RND_to_SP(level);
6030     CopyNativeTape_RND_to_SP(level);
6031
6032     SaveNativeLevel_SP(filename);
6033   }
6034 }
6035
6036
6037 /* ------------------------------------------------------------------------- */
6038 /* functions for loading generic level                                       */
6039 /* ------------------------------------------------------------------------- */
6040
6041 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6042                                   struct LevelFileInfo *level_file_info,
6043                                   boolean level_info_only)
6044 {
6045   /* always start with reliable default values */
6046   setLevelInfoToDefaults(level, level_info_only, TRUE);
6047
6048   switch (level_file_info->type)
6049   {
6050     case LEVEL_FILE_TYPE_RND:
6051       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6052       break;
6053
6054     case LEVEL_FILE_TYPE_EM:
6055       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6056       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6057       break;
6058
6059     case LEVEL_FILE_TYPE_SP:
6060       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6061       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6062       break;
6063
6064     case LEVEL_FILE_TYPE_MM:
6065       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6066       level->game_engine_type = GAME_ENGINE_TYPE_MM;
6067       break;
6068
6069     case LEVEL_FILE_TYPE_DC:
6070       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6071       break;
6072
6073     case LEVEL_FILE_TYPE_SB:
6074       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6075       break;
6076
6077     default:
6078       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6079       break;
6080   }
6081
6082   /* if level file is invalid, restore level structure to default values */
6083   if (level->no_valid_file)
6084     setLevelInfoToDefaults(level, level_info_only, FALSE);
6085
6086   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6087     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6088
6089   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6090     CopyNativeLevel_Native_to_RND(level);
6091 }
6092
6093 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6094 {
6095   static struct LevelFileInfo level_file_info;
6096
6097   /* always start with reliable default values */
6098   setFileInfoToDefaults(&level_file_info);
6099
6100   level_file_info.nr = 0;                       /* unknown level number */
6101   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
6102   level_file_info.filename = filename;
6103
6104   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6105 }
6106
6107 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6108 {
6109   int i, j;
6110
6111   if (leveldir_current == NULL)         /* only when dumping level */
6112     return;
6113
6114   /* all engine modifications also valid for levels which use latest engine */
6115   if (level->game_version < VERSION_IDENT(3,2,0,5))
6116   {
6117     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6118     level->score[SC_TIME_BONUS] /= 10;
6119   }
6120
6121   if (leveldir_current->latest_engine)
6122   {
6123     /* ---------- use latest game engine ----------------------------------- */
6124
6125     /* For all levels which are forced to use the latest game engine version
6126        (normally all but user contributed, private and undefined levels), set
6127        the game engine version to the actual version; this allows for actual
6128        corrections in the game engine to take effect for existing, converted
6129        levels (from "classic" or other existing games) to make the emulation
6130        of the corresponding game more accurate, while (hopefully) not breaking
6131        existing levels created from other players. */
6132
6133     level->game_version = GAME_VERSION_ACTUAL;
6134
6135     /* Set special EM style gems behaviour: EM style gems slip down from
6136        normal, steel and growing wall. As this is a more fundamental change,
6137        it seems better to set the default behaviour to "off" (as it is more
6138        natural) and make it configurable in the level editor (as a property
6139        of gem style elements). Already existing converted levels (neither
6140        private nor contributed levels) are changed to the new behaviour. */
6141
6142     if (level->file_version < FILE_VERSION_2_0)
6143       level->em_slippery_gems = TRUE;
6144
6145     return;
6146   }
6147
6148   /* ---------- use game engine the level was created with ----------------- */
6149
6150   /* For all levels which are not forced to use the latest game engine
6151      version (normally user contributed, private and undefined levels),
6152      use the version of the game engine the levels were created for.
6153
6154      Since 2.0.1, the game engine version is now directly stored
6155      in the level file (chunk "VERS"), so there is no need anymore
6156      to set the game version from the file version (except for old,
6157      pre-2.0 levels, where the game version is still taken from the
6158      file format version used to store the level -- see above). */
6159
6160   /* player was faster than enemies in 1.0.0 and before */
6161   if (level->file_version == FILE_VERSION_1_0)
6162     for (i = 0; i < MAX_PLAYERS; i++)
6163       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6164
6165   /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6166   if (level->game_version == VERSION_IDENT(2,0,1,0))
6167     level->em_slippery_gems = TRUE;
6168
6169   /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6170   if (level->game_version < VERSION_IDENT(2,2,0,0))
6171     level->use_spring_bug = TRUE;
6172
6173   if (level->game_version < VERSION_IDENT(3,2,0,5))
6174   {
6175     /* time orb caused limited time in endless time levels before 3.2.0-5 */
6176     level->use_time_orb_bug = TRUE;
6177
6178     /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6179     level->block_snap_field = FALSE;
6180
6181     /* extra time score was same value as time left score before 3.2.0-5 */
6182     level->extra_time_score = level->score[SC_TIME_BONUS];
6183   }
6184
6185   if (level->game_version < VERSION_IDENT(3,2,0,7))
6186   {
6187     /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6188     level->continuous_snapping = FALSE;
6189   }
6190
6191   /* only few elements were able to actively move into acid before 3.1.0 */
6192   /* trigger settings did not exist before 3.1.0; set to default "any" */
6193   if (level->game_version < VERSION_IDENT(3,1,0,0))
6194   {
6195     /* correct "can move into acid" settings (all zero in old levels) */
6196
6197     level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6198     level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6199
6200     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6201     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6202     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6203     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6204
6205     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6206       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6207
6208     /* correct trigger settings (stored as zero == "none" in old levels) */
6209
6210     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6211     {
6212       int element = EL_CUSTOM_START + i;
6213       struct ElementInfo *ei = &element_info[element];
6214
6215       for (j = 0; j < ei->num_change_pages; j++)
6216       {
6217         struct ElementChangeInfo *change = &ei->change_page[j];
6218
6219         change->trigger_player = CH_PLAYER_ANY;
6220         change->trigger_page = CH_PAGE_ANY;
6221       }
6222     }
6223   }
6224
6225   /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6226   {
6227     int element = EL_CUSTOM_256;
6228     struct ElementInfo *ei = &element_info[element];
6229     struct ElementChangeInfo *change = &ei->change_page[0];
6230
6231     /* This is needed to fix a problem that was caused by a bugfix in function
6232        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6233        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6234        not replace walkable elements, but instead just placed the player on it,
6235        without placing the Sokoban field under the player). Unfortunately, this
6236        breaks "Snake Bite" style levels when the snake is halfway through a door
6237        that just closes (the snake head is still alive and can be moved in this
6238        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6239        player (without Sokoban element) which then gets killed as designed). */
6240
6241     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6242          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6243         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6244       change->target_element = EL_PLAYER_1;
6245   }
6246
6247   /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6248   if (level->game_version < VERSION_IDENT(3,2,5,0))
6249   {
6250     /* This is needed to fix a problem that was caused by a bugfix in function
6251        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6252        corrects the behaviour when a custom element changes to another custom
6253        element with a higher element number that has change actions defined.
6254        Normally, only one change per frame is allowed for custom elements.
6255        Therefore, it is checked if a custom element already changed in the
6256        current frame; if it did, subsequent changes are suppressed.
6257        Unfortunately, this is only checked for element changes, but not for
6258        change actions, which are still executed. As the function above loops
6259        through all custom elements from lower to higher, an element change
6260        resulting in a lower CE number won't be checked again, while a target
6261        element with a higher number will also be checked, and potential change
6262        actions will get executed for this CE, too (which is wrong), while
6263        further changes are ignored (which is correct). As this bugfix breaks
6264        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6265        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6266        behaviour for existing levels and tapes that make use of this bug */
6267
6268     level->use_action_after_change_bug = TRUE;
6269   }
6270
6271   /* not centering level after relocating player was default only in 3.2.3 */
6272   if (level->game_version == VERSION_IDENT(3,2,3,0))    /* (no pre-releases) */
6273     level->shifted_relocation = TRUE;
6274
6275   /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6276   if (level->game_version < VERSION_IDENT(3,2,6,0))
6277     level->em_explodes_by_fire = TRUE;
6278 }
6279
6280 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6281 {
6282   int i, j, x, y;
6283
6284   /* map custom element change events that have changed in newer versions
6285      (these following values were accidentally changed in version 3.0.1)
6286      (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6287   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6288   {
6289     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6290     {
6291       int element = EL_CUSTOM_START + i;
6292
6293       /* order of checking and copying events to be mapped is important */
6294       /* (do not change the start and end value -- they are constant) */
6295       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6296       {
6297         if (HAS_CHANGE_EVENT(element, j - 2))
6298         {
6299           SET_CHANGE_EVENT(element, j - 2, FALSE);
6300           SET_CHANGE_EVENT(element, j, TRUE);
6301         }
6302       }
6303
6304       /* order of checking and copying events to be mapped is important */
6305       /* (do not change the start and end value -- they are constant) */
6306       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6307       {
6308         if (HAS_CHANGE_EVENT(element, j - 1))
6309         {
6310           SET_CHANGE_EVENT(element, j - 1, FALSE);
6311           SET_CHANGE_EVENT(element, j, TRUE);
6312         }
6313       }
6314     }
6315   }
6316
6317   /* initialize "can_change" field for old levels with only one change page */
6318   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6319   {
6320     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6321     {
6322       int element = EL_CUSTOM_START + i;
6323
6324       if (CAN_CHANGE(element))
6325         element_info[element].change->can_change = TRUE;
6326     }
6327   }
6328
6329   /* correct custom element values (for old levels without these options) */
6330   if (level->game_version < VERSION_IDENT(3,1,1,0))
6331   {
6332     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6333     {
6334       int element = EL_CUSTOM_START + i;
6335       struct ElementInfo *ei = &element_info[element];
6336
6337       if (ei->access_direction == MV_NO_DIRECTION)
6338         ei->access_direction = MV_ALL_DIRECTIONS;
6339     }
6340   }
6341
6342   /* correct custom element values (fix invalid values for all versions) */
6343   if (1)
6344   {
6345     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6346     {
6347       int element = EL_CUSTOM_START + i;
6348       struct ElementInfo *ei = &element_info[element];
6349
6350       for (j = 0; j < ei->num_change_pages; j++)
6351       {
6352         struct ElementChangeInfo *change = &ei->change_page[j];
6353
6354         if (change->trigger_player == CH_PLAYER_NONE)
6355           change->trigger_player = CH_PLAYER_ANY;
6356
6357         if (change->trigger_side == CH_SIDE_NONE)
6358           change->trigger_side = CH_SIDE_ANY;
6359       }
6360     }
6361   }
6362
6363   /* initialize "can_explode" field for old levels which did not store this */
6364   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6365   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6366   {
6367     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6368     {
6369       int element = EL_CUSTOM_START + i;
6370
6371       if (EXPLODES_1X1_OLD(element))
6372         element_info[element].explosion_type = EXPLODES_1X1;
6373
6374       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6375                                              EXPLODES_SMASHED(element) ||
6376                                              EXPLODES_IMPACT(element)));
6377     }
6378   }
6379
6380   /* correct previously hard-coded move delay values for maze runner style */
6381   if (level->game_version < VERSION_IDENT(3,1,1,0))
6382   {
6383     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6384     {
6385       int element = EL_CUSTOM_START + i;
6386
6387       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6388       {
6389         /* previously hard-coded and therefore ignored */
6390         element_info[element].move_delay_fixed = 9;
6391         element_info[element].move_delay_random = 0;
6392       }
6393     }
6394   }
6395
6396   /* map elements that have changed in newer versions */
6397   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6398                                                     level->game_version);
6399   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6400     for (x = 0; x < 3; x++)
6401       for (y = 0; y < 3; y++)
6402         level->yamyam_content[i].e[x][y] =
6403           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6404                                     level->game_version);
6405
6406   /* initialize element properties for level editor etc. */
6407   InitElementPropertiesEngine(level->game_version);
6408   InitElementPropertiesAfterLoading(level->game_version);
6409   InitElementPropertiesGfxElement();
6410 }
6411
6412 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6413 {
6414   int x, y;
6415
6416   /* map elements that have changed in newer versions */
6417   for (y = 0; y < level->fieldy; y++)
6418     for (x = 0; x < level->fieldx; x++)
6419       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6420                                                      level->game_version);
6421
6422   /* clear unused playfield data (nicer if level gets resized in editor) */
6423   for (x = 0; x < MAX_LEV_FIELDX; x++)
6424     for (y = 0; y < MAX_LEV_FIELDY; y++)
6425       if (x >= level->fieldx || y >= level->fieldy)
6426         level->field[x][y] = EL_EMPTY;
6427
6428   /* copy elements to runtime playfield array */
6429   for (x = 0; x < MAX_LEV_FIELDX; x++)
6430     for (y = 0; y < MAX_LEV_FIELDY; y++)
6431       Feld[x][y] = level->field[x][y];
6432
6433   /* initialize level size variables for faster access */
6434   lev_fieldx = level->fieldx;
6435   lev_fieldy = level->fieldy;
6436
6437   /* determine border element for this level */
6438   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6439     BorderElement = EL_EMPTY;   /* (in editor, SetBorderElement() is used) */
6440   else
6441     SetBorderElement();
6442 }
6443
6444 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6445 {
6446   struct LevelFileInfo *level_file_info = &level->file_info;
6447
6448   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6449     CopyNativeLevel_RND_to_Native(level);
6450 }
6451
6452 void LoadLevelTemplate(int nr)
6453 {
6454   char *filename;
6455
6456   setLevelFileInfo(&level_template.file_info, nr);
6457   filename = level_template.file_info.filename;
6458
6459   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6460
6461   LoadLevel_InitVersion(&level_template, filename);
6462   LoadLevel_InitElements(&level_template, filename);
6463
6464   ActivateLevelTemplate();
6465 }
6466
6467 void LoadLevel(int nr)
6468 {
6469   char *filename;
6470
6471   setLevelFileInfo(&level.file_info, nr);
6472   filename = level.file_info.filename;
6473
6474   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6475
6476   if (level.use_custom_template)
6477     LoadLevelTemplate(-1);
6478
6479   LoadLevel_InitVersion(&level, filename);
6480   LoadLevel_InitElements(&level, filename);
6481   LoadLevel_InitPlayfield(&level, filename);
6482
6483   LoadLevel_InitNativeEngines(&level, filename);
6484 }
6485
6486 void LoadLevelInfoOnly(int nr)
6487 {
6488   setLevelFileInfo(&level.file_info, nr);
6489
6490   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6491 }
6492
6493 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6494 {
6495   int chunk_size = 0;
6496
6497   chunk_size += putFileVersion(file, level->file_version);
6498   chunk_size += putFileVersion(file, level->game_version);
6499
6500   return chunk_size;
6501 }
6502
6503 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6504 {
6505   int chunk_size = 0;
6506
6507   chunk_size += putFile16BitBE(file, level->creation_date.year);
6508   chunk_size += putFile8Bit(file,    level->creation_date.month);
6509   chunk_size += putFile8Bit(file,    level->creation_date.day);
6510
6511   return chunk_size;
6512 }
6513
6514 #if ENABLE_HISTORIC_CHUNKS
6515 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6516 {
6517   int i, x, y;
6518
6519   putFile8Bit(file, level->fieldx);
6520   putFile8Bit(file, level->fieldy);
6521
6522   putFile16BitBE(file, level->time);
6523   putFile16BitBE(file, level->gems_needed);
6524
6525   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6526     putFile8Bit(file, level->name[i]);
6527
6528   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6529     putFile8Bit(file, level->score[i]);
6530
6531   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6532     for (y = 0; y < 3; y++)
6533       for (x = 0; x < 3; x++)
6534         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6535                            level->yamyam_content[i].e[x][y]));
6536   putFile8Bit(file, level->amoeba_speed);
6537   putFile8Bit(file, level->time_magic_wall);
6538   putFile8Bit(file, level->time_wheel);
6539   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6540                      level->amoeba_content));
6541   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6542   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6543   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6544   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6545
6546   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6547
6548   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6549   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6550   putFile32BitBE(file, level->can_move_into_acid_bits);
6551   putFile8Bit(file, level->dont_collide_with_bits);
6552
6553   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6554   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6555
6556   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6557   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6558   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6559
6560   putFile8Bit(file, level->game_engine_type);
6561
6562   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6563 }
6564 #endif
6565
6566 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6567 {
6568   int chunk_size = 0;
6569   int i;
6570
6571   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6572     chunk_size += putFile8Bit(file, level->name[i]);
6573
6574   return chunk_size;
6575 }
6576
6577 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6578 {
6579   int chunk_size = 0;
6580   int i;
6581
6582   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6583     chunk_size += putFile8Bit(file, level->author[i]);
6584
6585   return chunk_size;
6586 }
6587
6588 #if ENABLE_HISTORIC_CHUNKS
6589 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6590 {
6591   int chunk_size = 0;
6592   int x, y;
6593
6594   for (y = 0; y < level->fieldy; y++) 
6595     for (x = 0; x < level->fieldx; x++) 
6596       if (level->encoding_16bit_field)
6597         chunk_size += putFile16BitBE(file, level->field[x][y]);
6598       else
6599         chunk_size += putFile8Bit(file, level->field[x][y]);
6600
6601   return chunk_size;
6602 }
6603 #endif
6604
6605 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6606 {
6607   int chunk_size = 0;
6608   int x, y;
6609
6610   for (y = 0; y < level->fieldy; y++) 
6611     for (x = 0; x < level->fieldx; x++) 
6612       chunk_size += putFile16BitBE(file, level->field[x][y]);
6613
6614   return chunk_size;
6615 }
6616
6617 #if ENABLE_HISTORIC_CHUNKS
6618 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6619 {
6620   int i, x, y;
6621
6622   putFile8Bit(file, EL_YAMYAM);
6623   putFile8Bit(file, level->num_yamyam_contents);
6624   putFile8Bit(file, 0);
6625   putFile8Bit(file, 0);
6626
6627   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6628     for (y = 0; y < 3; y++)
6629       for (x = 0; x < 3; x++)
6630         if (level->encoding_16bit_field)
6631           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6632         else
6633           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6634 }
6635 #endif
6636
6637 #if ENABLE_HISTORIC_CHUNKS
6638 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6639 {
6640   int i, x, y;
6641   int num_contents, content_xsize, content_ysize;
6642   int content_array[MAX_ELEMENT_CONTENTS][3][3];
6643
6644   if (element == EL_YAMYAM)
6645   {
6646     num_contents = level->num_yamyam_contents;
6647     content_xsize = 3;
6648     content_ysize = 3;
6649
6650     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6651       for (y = 0; y < 3; y++)
6652         for (x = 0; x < 3; x++)
6653           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6654   }
6655   else if (element == EL_BD_AMOEBA)
6656   {
6657     num_contents = 1;
6658     content_xsize = 1;
6659     content_ysize = 1;
6660
6661     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6662       for (y = 0; y < 3; y++)
6663         for (x = 0; x < 3; x++)
6664           content_array[i][x][y] = EL_EMPTY;
6665     content_array[0][0][0] = level->amoeba_content;
6666   }
6667   else
6668   {
6669     /* chunk header already written -- write empty chunk data */
6670     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6671
6672     Error(ERR_WARN, "cannot save content for element '%d'", element);
6673     return;
6674   }
6675
6676   putFile16BitBE(file, element);
6677   putFile8Bit(file, num_contents);
6678   putFile8Bit(file, content_xsize);
6679   putFile8Bit(file, content_ysize);
6680
6681   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6682
6683   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6684     for (y = 0; y < 3; y++)
6685       for (x = 0; x < 3; x++)
6686         putFile16BitBE(file, content_array[i][x][y]);
6687 }
6688 #endif
6689
6690 #if ENABLE_HISTORIC_CHUNKS
6691 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6692 {
6693   int envelope_nr = element - EL_ENVELOPE_1;
6694   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6695   int chunk_size = 0;
6696   int i;
6697
6698   chunk_size += putFile16BitBE(file, element);
6699   chunk_size += putFile16BitBE(file, envelope_len);
6700   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6701   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6702
6703   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6704   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6705
6706   for (i = 0; i < envelope_len; i++)
6707     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6708
6709   return chunk_size;
6710 }
6711 #endif
6712
6713 #if ENABLE_HISTORIC_CHUNKS
6714 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6715                            int num_changed_custom_elements)
6716 {
6717   int i, check = 0;
6718
6719   putFile16BitBE(file, num_changed_custom_elements);
6720
6721   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6722   {
6723     int element = EL_CUSTOM_START + i;
6724
6725     struct ElementInfo *ei = &element_info[element];
6726
6727     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6728     {
6729       if (check < num_changed_custom_elements)
6730       {
6731         putFile16BitBE(file, element);
6732         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6733       }
6734
6735       check++;
6736     }
6737   }
6738
6739   if (check != num_changed_custom_elements)     /* should not happen */
6740     Error(ERR_WARN, "inconsistent number of custom element properties");
6741 }
6742 #endif
6743
6744 #if ENABLE_HISTORIC_CHUNKS
6745 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6746                            int num_changed_custom_elements)
6747 {
6748   int i, check = 0;
6749
6750   putFile16BitBE(file, num_changed_custom_elements);
6751
6752   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6753   {
6754     int element = EL_CUSTOM_START + i;
6755
6756     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6757     {
6758       if (check < num_changed_custom_elements)
6759       {
6760         putFile16BitBE(file, element);
6761         putFile16BitBE(file, element_info[element].change->target_element);
6762       }
6763
6764       check++;
6765     }
6766   }
6767
6768   if (check != num_changed_custom_elements)     /* should not happen */
6769     Error(ERR_WARN, "inconsistent number of custom target elements");
6770 }
6771 #endif
6772
6773 #if ENABLE_HISTORIC_CHUNKS
6774 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6775                            int num_changed_custom_elements)
6776 {
6777   int i, j, x, y, check = 0;
6778
6779   putFile16BitBE(file, num_changed_custom_elements);
6780
6781   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6782   {
6783     int element = EL_CUSTOM_START + i;
6784     struct ElementInfo *ei = &element_info[element];
6785
6786     if (ei->modified_settings)
6787     {
6788       if (check < num_changed_custom_elements)
6789       {
6790         putFile16BitBE(file, element);
6791
6792         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6793           putFile8Bit(file, ei->description[j]);
6794
6795         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6796
6797         /* some free bytes for future properties and padding */
6798         WriteUnusedBytesToFile(file, 7);
6799
6800         putFile8Bit(file, ei->use_gfx_element);
6801         putFile16BitBE(file, ei->gfx_element_initial);
6802
6803         putFile8Bit(file, ei->collect_score_initial);
6804         putFile8Bit(file, ei->collect_count_initial);
6805
6806         putFile16BitBE(file, ei->push_delay_fixed);
6807         putFile16BitBE(file, ei->push_delay_random);
6808         putFile16BitBE(file, ei->move_delay_fixed);
6809         putFile16BitBE(file, ei->move_delay_random);
6810
6811         putFile16BitBE(file, ei->move_pattern);
6812         putFile8Bit(file, ei->move_direction_initial);
6813         putFile8Bit(file, ei->move_stepsize);
6814
6815         for (y = 0; y < 3; y++)
6816           for (x = 0; x < 3; x++)
6817             putFile16BitBE(file, ei->content.e[x][y]);
6818
6819         putFile32BitBE(file, ei->change->events);
6820
6821         putFile16BitBE(file, ei->change->target_element);
6822
6823         putFile16BitBE(file, ei->change->delay_fixed);
6824         putFile16BitBE(file, ei->change->delay_random);
6825         putFile16BitBE(file, ei->change->delay_frames);
6826
6827         putFile16BitBE(file, ei->change->initial_trigger_element);
6828
6829         putFile8Bit(file, ei->change->explode);
6830         putFile8Bit(file, ei->change->use_target_content);
6831         putFile8Bit(file, ei->change->only_if_complete);
6832         putFile8Bit(file, ei->change->use_random_replace);
6833
6834         putFile8Bit(file, ei->change->random_percentage);
6835         putFile8Bit(file, ei->change->replace_when);
6836
6837         for (y = 0; y < 3; y++)
6838           for (x = 0; x < 3; x++)
6839             putFile16BitBE(file, ei->change->content.e[x][y]);
6840
6841         putFile8Bit(file, ei->slippery_type);
6842
6843         /* some free bytes for future properties and padding */
6844         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6845       }
6846
6847       check++;
6848     }
6849   }
6850
6851   if (check != num_changed_custom_elements)     /* should not happen */
6852     Error(ERR_WARN, "inconsistent number of custom element properties");
6853 }
6854 #endif
6855
6856 #if ENABLE_HISTORIC_CHUNKS
6857 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6858 {
6859   struct ElementInfo *ei = &element_info[element];
6860   int i, j, x, y;
6861
6862   /* ---------- custom element base property values (96 bytes) ------------- */
6863
6864   putFile16BitBE(file, element);
6865
6866   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6867     putFile8Bit(file, ei->description[i]);
6868
6869   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6870
6871   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
6872
6873   putFile8Bit(file, ei->num_change_pages);
6874
6875   putFile16BitBE(file, ei->ce_value_fixed_initial);
6876   putFile16BitBE(file, ei->ce_value_random_initial);
6877   putFile8Bit(file, ei->use_last_ce_value);
6878
6879   putFile8Bit(file, ei->use_gfx_element);
6880   putFile16BitBE(file, ei->gfx_element_initial);
6881
6882   putFile8Bit(file, ei->collect_score_initial);
6883   putFile8Bit(file, ei->collect_count_initial);
6884
6885   putFile8Bit(file, ei->drop_delay_fixed);
6886   putFile8Bit(file, ei->push_delay_fixed);
6887   putFile8Bit(file, ei->drop_delay_random);
6888   putFile8Bit(file, ei->push_delay_random);
6889   putFile16BitBE(file, ei->move_delay_fixed);
6890   putFile16BitBE(file, ei->move_delay_random);
6891
6892   /* bits 0 - 15 of "move_pattern" ... */
6893   putFile16BitBE(file, ei->move_pattern & 0xffff);
6894   putFile8Bit(file, ei->move_direction_initial);
6895   putFile8Bit(file, ei->move_stepsize);
6896
6897   putFile8Bit(file, ei->slippery_type);
6898
6899   for (y = 0; y < 3; y++)
6900     for (x = 0; x < 3; x++)
6901       putFile16BitBE(file, ei->content.e[x][y]);
6902
6903   putFile16BitBE(file, ei->move_enter_element);
6904   putFile16BitBE(file, ei->move_leave_element);
6905   putFile8Bit(file, ei->move_leave_type);
6906
6907   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6908   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6909
6910   putFile8Bit(file, ei->access_direction);
6911
6912   putFile8Bit(file, ei->explosion_delay);
6913   putFile8Bit(file, ei->ignition_delay);
6914   putFile8Bit(file, ei->explosion_type);
6915
6916   /* some free bytes for future custom property values and padding */
6917   WriteUnusedBytesToFile(file, 1);
6918
6919   /* ---------- change page property values (48 bytes) --------------------- */
6920
6921   for (i = 0; i < ei->num_change_pages; i++)
6922   {
6923     struct ElementChangeInfo *change = &ei->change_page[i];
6924     unsigned int event_bits;
6925
6926     /* bits 0 - 31 of "has_event[]" ... */
6927     event_bits = 0;
6928     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6929       if (change->has_event[j])
6930         event_bits |= (1 << j);
6931     putFile32BitBE(file, event_bits);
6932
6933     putFile16BitBE(file, change->target_element);
6934
6935     putFile16BitBE(file, change->delay_fixed);
6936     putFile16BitBE(file, change->delay_random);
6937     putFile16BitBE(file, change->delay_frames);
6938
6939     putFile16BitBE(file, change->initial_trigger_element);
6940
6941     putFile8Bit(file, change->explode);
6942     putFile8Bit(file, change->use_target_content);
6943     putFile8Bit(file, change->only_if_complete);
6944     putFile8Bit(file, change->use_random_replace);
6945
6946     putFile8Bit(file, change->random_percentage);
6947     putFile8Bit(file, change->replace_when);
6948
6949     for (y = 0; y < 3; y++)
6950       for (x = 0; x < 3; x++)
6951         putFile16BitBE(file, change->target_content.e[x][y]);
6952
6953     putFile8Bit(file, change->can_change);
6954
6955     putFile8Bit(file, change->trigger_side);
6956
6957     putFile8Bit(file, change->trigger_player);
6958     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6959                        log_2(change->trigger_page)));
6960
6961     putFile8Bit(file, change->has_action);
6962     putFile8Bit(file, change->action_type);
6963     putFile8Bit(file, change->action_mode);
6964     putFile16BitBE(file, change->action_arg);
6965
6966     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6967     event_bits = 0;
6968     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6969       if (change->has_event[j])
6970         event_bits |= (1 << (j - 32));
6971     putFile8Bit(file, event_bits);
6972   }
6973 }
6974 #endif
6975
6976 #if ENABLE_HISTORIC_CHUNKS
6977 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6978 {
6979   struct ElementInfo *ei = &element_info[element];
6980   struct ElementGroupInfo *group = ei->group;
6981   int i;
6982
6983   putFile16BitBE(file, element);
6984
6985   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6986     putFile8Bit(file, ei->description[i]);
6987
6988   putFile8Bit(file, group->num_elements);
6989
6990   putFile8Bit(file, ei->use_gfx_element);
6991   putFile16BitBE(file, ei->gfx_element_initial);
6992
6993   putFile8Bit(file, group->choice_mode);
6994
6995   /* some free bytes for future values and padding */
6996   WriteUnusedBytesToFile(file, 3);
6997
6998   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6999     putFile16BitBE(file, group->element[i]);
7000 }
7001 #endif
7002
7003 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7004                                 boolean write_element)
7005 {
7006   int save_type = entry->save_type;
7007   int data_type = entry->data_type;
7008   int conf_type = entry->conf_type;
7009   int byte_mask = conf_type & CONF_MASK_BYTES;
7010   int element = entry->element;
7011   int default_value = entry->default_value;
7012   int num_bytes = 0;
7013   boolean modified = FALSE;
7014
7015   if (byte_mask != CONF_MASK_MULTI_BYTES)
7016   {
7017     void *value_ptr = entry->value;
7018     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7019                  *(int *)value_ptr);
7020
7021     /* check if any settings have been modified before saving them */
7022     if (value != default_value)
7023       modified = TRUE;
7024
7025     /* do not save if explicitly told or if unmodified default settings */
7026     if ((save_type == SAVE_CONF_NEVER) ||
7027         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7028       return 0;
7029
7030     if (write_element)
7031       num_bytes += putFile16BitBE(file, element);
7032
7033     num_bytes += putFile8Bit(file, conf_type);
7034     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7035                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7036                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7037                   0);
7038   }
7039   else if (data_type == TYPE_STRING)
7040   {
7041     char *default_string = entry->default_string;
7042     char *string = (char *)(entry->value);
7043     int string_length = strlen(string);
7044     int i;
7045
7046     /* check if any settings have been modified before saving them */
7047     if (!strEqual(string, default_string))
7048       modified = TRUE;
7049
7050     /* do not save if explicitly told or if unmodified default settings */
7051     if ((save_type == SAVE_CONF_NEVER) ||
7052         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7053       return 0;
7054
7055     if (write_element)
7056       num_bytes += putFile16BitBE(file, element);
7057
7058     num_bytes += putFile8Bit(file, conf_type);
7059     num_bytes += putFile16BitBE(file, string_length);
7060
7061     for (i = 0; i < string_length; i++)
7062       num_bytes += putFile8Bit(file, string[i]);
7063   }
7064   else if (data_type == TYPE_ELEMENT_LIST)
7065   {
7066     int *element_array = (int *)(entry->value);
7067     int num_elements = *(int *)(entry->num_entities);
7068     int i;
7069
7070     /* check if any settings have been modified before saving them */
7071     for (i = 0; i < num_elements; i++)
7072       if (element_array[i] != default_value)
7073         modified = TRUE;
7074
7075     /* do not save if explicitly told or if unmodified default settings */
7076     if ((save_type == SAVE_CONF_NEVER) ||
7077         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7078       return 0;
7079
7080     if (write_element)
7081       num_bytes += putFile16BitBE(file, element);
7082
7083     num_bytes += putFile8Bit(file, conf_type);
7084     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7085
7086     for (i = 0; i < num_elements; i++)
7087       num_bytes += putFile16BitBE(file, element_array[i]);
7088   }
7089   else if (data_type == TYPE_CONTENT_LIST)
7090   {
7091     struct Content *content = (struct Content *)(entry->value);
7092     int num_contents = *(int *)(entry->num_entities);
7093     int i, x, y;
7094
7095     /* check if any settings have been modified before saving them */
7096     for (i = 0; i < num_contents; i++)
7097       for (y = 0; y < 3; y++)
7098         for (x = 0; x < 3; x++)
7099           if (content[i].e[x][y] != default_value)
7100             modified = TRUE;
7101
7102     /* do not save if explicitly told or if unmodified default settings */
7103     if ((save_type == SAVE_CONF_NEVER) ||
7104         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7105       return 0;
7106
7107     if (write_element)
7108       num_bytes += putFile16BitBE(file, element);
7109
7110     num_bytes += putFile8Bit(file, conf_type);
7111     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7112
7113     for (i = 0; i < num_contents; i++)
7114       for (y = 0; y < 3; y++)
7115         for (x = 0; x < 3; x++)
7116           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7117   }
7118
7119   return num_bytes;
7120 }
7121
7122 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7123 {
7124   int chunk_size = 0;
7125   int i;
7126
7127   li = *level;          /* copy level data into temporary buffer */
7128
7129   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7130     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7131
7132   return chunk_size;
7133 }
7134
7135 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7136 {
7137   int chunk_size = 0;
7138   int i;
7139
7140   li = *level;          /* copy level data into temporary buffer */
7141
7142   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7143     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7144
7145   return chunk_size;
7146 }
7147
7148 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7149 {
7150   int envelope_nr = element - EL_ENVELOPE_1;
7151   int chunk_size = 0;
7152   int i;
7153
7154   chunk_size += putFile16BitBE(file, element);
7155
7156   /* copy envelope data into temporary buffer */
7157   xx_envelope = level->envelope[envelope_nr];
7158
7159   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7160     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7161
7162   return chunk_size;
7163 }
7164
7165 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7166 {
7167   struct ElementInfo *ei = &element_info[element];
7168   int chunk_size = 0;
7169   int i, j;
7170
7171   chunk_size += putFile16BitBE(file, element);
7172
7173   xx_ei = *ei;          /* copy element data into temporary buffer */
7174
7175   /* set default description string for this specific element */
7176   strcpy(xx_default_description, getDefaultElementDescription(ei));
7177
7178   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7179     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7180
7181   for (i = 0; i < ei->num_change_pages; i++)
7182   {
7183     struct ElementChangeInfo *change = &ei->change_page[i];
7184
7185     xx_current_change_page = i;
7186
7187     xx_change = *change;        /* copy change data into temporary buffer */
7188
7189     resetEventBits();
7190     setEventBitsFromEventFlags(change);
7191
7192     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7193       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7194                                          FALSE);
7195   }
7196
7197   return chunk_size;
7198 }
7199
7200 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7201 {
7202   struct ElementInfo *ei = &element_info[element];
7203   struct ElementGroupInfo *group = ei->group;
7204   int chunk_size = 0;
7205   int i;
7206
7207   chunk_size += putFile16BitBE(file, element);
7208
7209   xx_ei = *ei;          /* copy element data into temporary buffer */
7210   xx_group = *group;    /* copy group data into temporary buffer */
7211
7212   /* set default description string for this specific element */
7213   strcpy(xx_default_description, getDefaultElementDescription(ei));
7214
7215   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7216     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7217
7218   return chunk_size;
7219 }
7220
7221 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7222                                   boolean save_as_template)
7223 {
7224   int chunk_size;
7225   int i;
7226   FILE *file;
7227
7228   if (!(file = fopen(filename, MODE_WRITE)))
7229   {
7230     Error(ERR_WARN, "cannot save level file '%s'", filename);
7231     return;
7232   }
7233
7234   level->file_version = FILE_VERSION_ACTUAL;
7235   level->game_version = GAME_VERSION_ACTUAL;
7236
7237   level->creation_date = getCurrentDate();
7238
7239   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7240   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7241
7242   chunk_size = SaveLevel_VERS(NULL, level);
7243   putFileChunkBE(file, "VERS", chunk_size);
7244   SaveLevel_VERS(file, level);
7245
7246   chunk_size = SaveLevel_DATE(NULL, level);
7247   putFileChunkBE(file, "DATE", chunk_size);
7248   SaveLevel_DATE(file, level);
7249
7250   chunk_size = SaveLevel_NAME(NULL, level);
7251   putFileChunkBE(file, "NAME", chunk_size);
7252   SaveLevel_NAME(file, level);
7253
7254   chunk_size = SaveLevel_AUTH(NULL, level);
7255   putFileChunkBE(file, "AUTH", chunk_size);
7256   SaveLevel_AUTH(file, level);
7257
7258   chunk_size = SaveLevel_INFO(NULL, level);
7259   putFileChunkBE(file, "INFO", chunk_size);
7260   SaveLevel_INFO(file, level);
7261
7262   chunk_size = SaveLevel_BODY(NULL, level);
7263   putFileChunkBE(file, "BODY", chunk_size);
7264   SaveLevel_BODY(file, level);
7265
7266   chunk_size = SaveLevel_ELEM(NULL, level);
7267   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          /* save if changed */
7268   {
7269     putFileChunkBE(file, "ELEM", chunk_size);
7270     SaveLevel_ELEM(file, level);
7271   }
7272
7273   for (i = 0; i < NUM_ENVELOPES; i++)
7274   {
7275     int element = EL_ENVELOPE_1 + i;
7276
7277     chunk_size = SaveLevel_NOTE(NULL, level, element);
7278     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        /* save if changed */
7279     {
7280       putFileChunkBE(file, "NOTE", chunk_size);
7281       SaveLevel_NOTE(file, level, element);
7282     }
7283   }
7284
7285   /* if not using template level, check for non-default custom/group elements */
7286   if (!level->use_custom_template || save_as_template)
7287   {
7288     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7289     {
7290       int element = EL_CUSTOM_START + i;
7291
7292       chunk_size = SaveLevel_CUSX(NULL, level, element);
7293       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      /* save if changed */
7294       {
7295         putFileChunkBE(file, "CUSX", chunk_size);
7296         SaveLevel_CUSX(file, level, element);
7297       }
7298     }
7299
7300     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7301     {
7302       int element = EL_GROUP_START + i;
7303
7304       chunk_size = SaveLevel_GRPX(NULL, level, element);
7305       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      /* save if changed */
7306       {
7307         putFileChunkBE(file, "GRPX", chunk_size);
7308         SaveLevel_GRPX(file, level, element);
7309       }
7310     }
7311   }
7312
7313   fclose(file);
7314
7315   SetFilePermissions(filename, PERMS_PRIVATE);
7316 }
7317
7318 void SaveLevel(int nr)
7319 {
7320   char *filename = getDefaultLevelFilename(nr);
7321
7322   SaveLevelFromFilename(&level, filename, FALSE);
7323 }
7324
7325 void SaveLevelTemplate()
7326 {
7327   char *filename = getLocalLevelTemplateFilename();
7328
7329   SaveLevelFromFilename(&level, filename, TRUE);
7330 }
7331
7332 boolean SaveLevelChecked(int nr)
7333 {
7334   char *filename = getDefaultLevelFilename(nr);
7335   boolean new_level = !fileExists(filename);
7336   boolean level_saved = FALSE;
7337
7338   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7339   {
7340     SaveLevel(nr);
7341
7342     if (new_level)
7343       Request("Level saved!", REQ_CONFIRM);
7344
7345     level_saved = TRUE;
7346   }
7347
7348   return level_saved;
7349 }
7350
7351 void DumpLevel(struct LevelInfo *level)
7352 {
7353   if (level->no_level_file || level->no_valid_file)
7354   {
7355     Error(ERR_WARN, "cannot dump -- no valid level file found");
7356
7357     return;
7358   }
7359
7360   PrintLine("-", 79);
7361   Print("Level xxx (file version %08d, game version %08d)\n",
7362         level->file_version, level->game_version);
7363   PrintLine("-", 79);
7364
7365   Print("Level author: '%s'\n", level->author);
7366   Print("Level title:  '%s'\n", level->name);
7367   Print("\n");
7368   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7369   Print("\n");
7370   Print("Level time:  %d seconds\n", level->time);
7371   Print("Gems needed: %d\n", level->gems_needed);
7372   Print("\n");
7373   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7374   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7375   Print("Time for light:      %d seconds\n", level->time_light);
7376   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7377   Print("\n");
7378   Print("Amoeba speed: %d\n", level->amoeba_speed);
7379   Print("\n");
7380
7381   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7382   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7383   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7384   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7385   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7386
7387   PrintLine("-", 79);
7388 }
7389
7390
7391 /* ========================================================================= */
7392 /* tape file functions                                                       */
7393 /* ========================================================================= */
7394
7395 static void setTapeInfoToDefaults()
7396 {
7397   int i;
7398
7399   /* always start with reliable default values (empty tape) */
7400   TapeErase();
7401
7402   /* default values (also for pre-1.2 tapes) with only the first player */
7403   tape.player_participates[0] = TRUE;
7404   for (i = 1; i < MAX_PLAYERS; i++)
7405     tape.player_participates[i] = FALSE;
7406
7407   /* at least one (default: the first) player participates in every tape */
7408   tape.num_participating_players = 1;
7409
7410   tape.level_nr = level_nr;
7411   tape.counter = 0;
7412   tape.changed = FALSE;
7413
7414   tape.recording = FALSE;
7415   tape.playing = FALSE;
7416   tape.pausing = FALSE;
7417
7418   tape.no_valid_file = FALSE;
7419 }
7420
7421 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7422 {
7423   tape->file_version = getFileVersion(file);
7424   tape->game_version = getFileVersion(file);
7425
7426   return chunk_size;
7427 }
7428
7429 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7430 {
7431   int i;
7432
7433   tape->random_seed = getFile32BitBE(file);
7434   tape->date        = getFile32BitBE(file);
7435   tape->length      = getFile32BitBE(file);
7436
7437   /* read header fields that are new since version 1.2 */
7438   if (tape->file_version >= FILE_VERSION_1_2)
7439   {
7440     byte store_participating_players = getFile8Bit(file);
7441     int engine_version;
7442
7443     /* since version 1.2, tapes store which players participate in the tape */
7444     tape->num_participating_players = 0;
7445     for (i = 0; i < MAX_PLAYERS; i++)
7446     {
7447       tape->player_participates[i] = FALSE;
7448
7449       if (store_participating_players & (1 << i))
7450       {
7451         tape->player_participates[i] = TRUE;
7452         tape->num_participating_players++;
7453       }
7454     }
7455
7456     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7457
7458     engine_version = getFileVersion(file);
7459     if (engine_version > 0)
7460       tape->engine_version = engine_version;
7461     else
7462       tape->engine_version = tape->game_version;
7463   }
7464
7465   return chunk_size;
7466 }
7467
7468 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7469 {
7470   int level_identifier_size;
7471   int i;
7472
7473   level_identifier_size = getFile16BitBE(file);
7474
7475   tape->level_identifier =
7476     checked_realloc(tape->level_identifier, level_identifier_size);
7477
7478   for (i = 0; i < level_identifier_size; i++)
7479     tape->level_identifier[i] = getFile8Bit(file);
7480
7481   tape->level_nr = getFile16BitBE(file);
7482
7483   chunk_size = 2 + level_identifier_size + 2;
7484
7485   return chunk_size;
7486 }
7487
7488 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7489 {
7490   int i, j;
7491   int chunk_size_expected =
7492     (tape->num_participating_players + 1) * tape->length;
7493
7494   if (chunk_size_expected != chunk_size)
7495   {
7496     ReadUnusedBytesFromFile(file, chunk_size);
7497     return chunk_size_expected;
7498   }
7499
7500   for (i = 0; i < tape->length; i++)
7501   {
7502     if (i >= MAX_TAPE_LEN)
7503     {
7504       Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7505             MAX_TAPE_LEN);
7506
7507       // tape too large; read and ignore remaining tape data from this chunk
7508       for (;i < tape->length; i++)
7509         ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7510
7511       break;
7512     }
7513
7514     for (j = 0; j < MAX_PLAYERS; j++)
7515     {
7516       tape->pos[i].action[j] = MV_NONE;
7517
7518       if (tape->player_participates[j])
7519         tape->pos[i].action[j] = getFile8Bit(file);
7520     }
7521
7522     tape->pos[i].delay = getFile8Bit(file);
7523
7524     if (tape->file_version == FILE_VERSION_1_0)
7525     {
7526       /* eliminate possible diagonal moves in old tapes */
7527       /* this is only for backward compatibility */
7528
7529       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7530       byte action = tape->pos[i].action[0];
7531       int k, num_moves = 0;
7532
7533       for (k = 0; k<4; k++)
7534       {
7535         if (action & joy_dir[k])
7536         {
7537           tape->pos[i + num_moves].action[0] = joy_dir[k];
7538           if (num_moves > 0)
7539             tape->pos[i + num_moves].delay = 0;
7540           num_moves++;
7541         }
7542       }
7543
7544       if (num_moves > 1)
7545       {
7546         num_moves--;
7547         i += num_moves;
7548         tape->length += num_moves;
7549       }
7550     }
7551     else if (tape->file_version < FILE_VERSION_2_0)
7552     {
7553       /* convert pre-2.0 tapes to new tape format */
7554
7555       if (tape->pos[i].delay > 1)
7556       {
7557         /* action part */
7558         tape->pos[i + 1] = tape->pos[i];
7559         tape->pos[i + 1].delay = 1;
7560
7561         /* delay part */
7562         for (j = 0; j < MAX_PLAYERS; j++)
7563           tape->pos[i].action[j] = MV_NONE;
7564         tape->pos[i].delay--;
7565
7566         i++;
7567         tape->length++;
7568       }
7569     }
7570
7571     if (checkEndOfFile(file))
7572       break;
7573   }
7574
7575   if (i != tape->length)
7576     chunk_size = (tape->num_participating_players + 1) * i;
7577
7578   return chunk_size;
7579 }
7580
7581 void LoadTape_SokobanSolution(char *filename)
7582 {
7583   File *file;
7584   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7585
7586   if (!(file = openFile(filename, MODE_READ)))
7587   {
7588     tape.no_valid_file = TRUE;
7589
7590     return;
7591   }
7592
7593   while (!checkEndOfFile(file))
7594   {
7595     unsigned char c = getByteFromFile(file);
7596
7597     if (checkEndOfFile(file))
7598       break;
7599
7600     switch (c)
7601     {
7602       case 'u':
7603       case 'U':
7604         tape.pos[tape.length].action[0] = MV_UP;
7605         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7606         tape.length++;
7607         break;
7608
7609       case 'd':
7610       case 'D':
7611         tape.pos[tape.length].action[0] = MV_DOWN;
7612         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7613         tape.length++;
7614         break;
7615
7616       case 'l':
7617       case 'L':
7618         tape.pos[tape.length].action[0] = MV_LEFT;
7619         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7620         tape.length++;
7621         break;
7622
7623       case 'r':
7624       case 'R':
7625         tape.pos[tape.length].action[0] = MV_RIGHT;
7626         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7627         tape.length++;
7628         break;
7629
7630       case '\n':
7631       case '\r':
7632       case '\t':
7633       case ' ':
7634         /* ignore white-space characters */
7635         break;
7636
7637       default:
7638         tape.no_valid_file = TRUE;
7639
7640         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7641
7642         break;
7643     }
7644   }
7645
7646   closeFile(file);
7647
7648   if (tape.no_valid_file)
7649     return;
7650
7651   tape.length_frames  = GetTapeLengthFrames();
7652   tape.length_seconds = GetTapeLengthSeconds();
7653 }
7654
7655 void LoadTapeFromFilename(char *filename)
7656 {
7657   char cookie[MAX_LINE_LEN];
7658   char chunk_name[CHUNK_ID_LEN + 1];
7659   File *file;
7660   int chunk_size;
7661
7662   /* always start with reliable default values */
7663   setTapeInfoToDefaults();
7664
7665   if (strSuffix(filename, ".sln"))
7666   {
7667     LoadTape_SokobanSolution(filename);
7668
7669     return;
7670   }
7671
7672   if (!(file = openFile(filename, MODE_READ)))
7673   {
7674     tape.no_valid_file = TRUE;
7675
7676     return;
7677   }
7678
7679   getFileChunkBE(file, chunk_name, NULL);
7680   if (strEqual(chunk_name, "RND1"))
7681   {
7682     getFile32BitBE(file);               /* not used */
7683
7684     getFileChunkBE(file, chunk_name, NULL);
7685     if (!strEqual(chunk_name, "TAPE"))
7686     {
7687       tape.no_valid_file = TRUE;
7688
7689       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7690
7691       closeFile(file);
7692
7693       return;
7694     }
7695   }
7696   else  /* check for pre-2.0 file format with cookie string */
7697   {
7698     strcpy(cookie, chunk_name);
7699     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7700       cookie[4] = '\0';
7701     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7702       cookie[strlen(cookie) - 1] = '\0';
7703
7704     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7705     {
7706       tape.no_valid_file = TRUE;
7707
7708       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7709
7710       closeFile(file);
7711
7712       return;
7713     }
7714
7715     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7716     {
7717       tape.no_valid_file = TRUE;
7718
7719       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7720
7721       closeFile(file);
7722
7723       return;
7724     }
7725
7726     /* pre-2.0 tape files have no game version, so use file version here */
7727     tape.game_version = tape.file_version;
7728   }
7729
7730   if (tape.file_version < FILE_VERSION_1_2)
7731   {
7732     /* tape files from versions before 1.2.0 without chunk structure */
7733     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7734     LoadTape_BODY(file, 2 * tape.length,      &tape);
7735   }
7736   else
7737   {
7738     static struct
7739     {
7740       char *name;
7741       int size;
7742       int (*loader)(File *, int, struct TapeInfo *);
7743     }
7744     chunk_info[] =
7745     {
7746       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
7747       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
7748       { "INFO", -1,                     LoadTape_INFO },
7749       { "BODY", -1,                     LoadTape_BODY },
7750       {  NULL,  0,                      NULL }
7751     };
7752
7753     while (getFileChunkBE(file, chunk_name, &chunk_size))
7754     {
7755       int i = 0;
7756
7757       while (chunk_info[i].name != NULL &&
7758              !strEqual(chunk_name, chunk_info[i].name))
7759         i++;
7760
7761       if (chunk_info[i].name == NULL)
7762       {
7763         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7764               chunk_name, filename);
7765         ReadUnusedBytesFromFile(file, chunk_size);
7766       }
7767       else if (chunk_info[i].size != -1 &&
7768                chunk_info[i].size != chunk_size)
7769       {
7770         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7771               chunk_size, chunk_name, filename);
7772         ReadUnusedBytesFromFile(file, chunk_size);
7773       }
7774       else
7775       {
7776         /* call function to load this tape chunk */
7777         int chunk_size_expected =
7778           (chunk_info[i].loader)(file, chunk_size, &tape);
7779
7780         /* the size of some chunks cannot be checked before reading other
7781            chunks first (like "HEAD" and "BODY") that contain some header
7782            information, so check them here */
7783         if (chunk_size_expected != chunk_size)
7784         {
7785           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7786                 chunk_size, chunk_name, filename);
7787         }
7788       }
7789     }
7790   }
7791
7792   closeFile(file);
7793
7794   tape.length_frames  = GetTapeLengthFrames();
7795   tape.length_seconds = GetTapeLengthSeconds();
7796
7797 #if 0
7798   printf("::: tape file version: %d\n",   tape.file_version);
7799   printf("::: tape game version: %d\n",   tape.game_version);
7800   printf("::: tape engine version: %d\n", tape.engine_version);
7801 #endif
7802 }
7803
7804 void LoadTape(int nr)
7805 {
7806   char *filename = getTapeFilename(nr);
7807
7808   LoadTapeFromFilename(filename);
7809 }
7810
7811 void LoadSolutionTape(int nr)
7812 {
7813   char *filename = getSolutionTapeFilename(nr);
7814
7815   LoadTapeFromFilename(filename);
7816
7817   if (TAPE_IS_EMPTY(tape) &&
7818       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7819       level.native_sp_level->demo.is_available)
7820     CopyNativeTape_SP_to_RND(&level);
7821 }
7822
7823 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7824 {
7825   putFileVersion(file, tape->file_version);
7826   putFileVersion(file, tape->game_version);
7827 }
7828
7829 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7830 {
7831   int i;
7832   byte store_participating_players = 0;
7833
7834   /* set bits for participating players for compact storage */
7835   for (i = 0; i < MAX_PLAYERS; i++)
7836     if (tape->player_participates[i])
7837       store_participating_players |= (1 << i);
7838
7839   putFile32BitBE(file, tape->random_seed);
7840   putFile32BitBE(file, tape->date);
7841   putFile32BitBE(file, tape->length);
7842
7843   putFile8Bit(file, store_participating_players);
7844
7845   /* unused bytes not at the end here for 4-byte alignment of engine_version */
7846   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7847
7848   putFileVersion(file, tape->engine_version);
7849 }
7850
7851 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7852 {
7853   int level_identifier_size = strlen(tape->level_identifier) + 1;
7854   int i;
7855
7856   putFile16BitBE(file, level_identifier_size);
7857
7858   for (i = 0; i < level_identifier_size; i++)
7859     putFile8Bit(file, tape->level_identifier[i]);
7860
7861   putFile16BitBE(file, tape->level_nr);
7862 }
7863
7864 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7865 {
7866   int i, j;
7867
7868   for (i = 0; i < tape->length; i++)
7869   {
7870     for (j = 0; j < MAX_PLAYERS; j++)
7871       if (tape->player_participates[j])
7872         putFile8Bit(file, tape->pos[i].action[j]);
7873
7874     putFile8Bit(file, tape->pos[i].delay);
7875   }
7876 }
7877
7878 void SaveTape(int nr)
7879 {
7880   char *filename = getTapeFilename(nr);
7881   FILE *file;
7882   int num_participating_players = 0;
7883   int info_chunk_size;
7884   int body_chunk_size;
7885   int i;
7886
7887   InitTapeDirectory(leveldir_current->subdir);
7888
7889   if (!(file = fopen(filename, MODE_WRITE)))
7890   {
7891     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7892     return;
7893   }
7894
7895   tape.file_version = FILE_VERSION_ACTUAL;
7896   tape.game_version = GAME_VERSION_ACTUAL;
7897
7898   /* count number of participating players  */
7899   for (i = 0; i < MAX_PLAYERS; i++)
7900     if (tape.player_participates[i])
7901       num_participating_players++;
7902
7903   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7904   body_chunk_size = (num_participating_players + 1) * tape.length;
7905
7906   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7907   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7908
7909   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7910   SaveTape_VERS(file, &tape);
7911
7912   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7913   SaveTape_HEAD(file, &tape);
7914
7915   putFileChunkBE(file, "INFO", info_chunk_size);
7916   SaveTape_INFO(file, &tape);
7917
7918   putFileChunkBE(file, "BODY", body_chunk_size);
7919   SaveTape_BODY(file, &tape);
7920
7921   fclose(file);
7922
7923   SetFilePermissions(filename, PERMS_PRIVATE);
7924
7925   tape.changed = FALSE;
7926 }
7927
7928 boolean SaveTapeChecked(int nr)
7929 {
7930   char *filename = getTapeFilename(nr);
7931   boolean new_tape = !fileExists(filename);
7932   boolean tape_saved = FALSE;
7933
7934   if (new_tape || Request("Replace old tape?", REQ_ASK))
7935   {
7936     SaveTape(nr);
7937
7938     if (new_tape)
7939       Request("Tape saved!", REQ_CONFIRM);
7940
7941     tape_saved = TRUE;
7942   }
7943
7944   return tape_saved;
7945 }
7946
7947 void DumpTape(struct TapeInfo *tape)
7948 {
7949   int tape_frame_counter;
7950   int i, j;
7951
7952   if (tape->no_valid_file)
7953   {
7954     Error(ERR_WARN, "cannot dump -- no valid tape file found");
7955
7956     return;
7957   }
7958
7959   PrintLine("-", 79);
7960   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
7961         tape->level_nr, tape->file_version, tape->game_version);
7962   Print("                  (effective engine version %08d)\n",
7963         tape->engine_version);
7964   Print("Level series identifier: '%s'\n", tape->level_identifier);
7965   PrintLine("-", 79);
7966
7967   tape_frame_counter = 0;
7968
7969   for (i = 0; i < tape->length; i++)
7970   {
7971     if (i >= MAX_TAPE_LEN)
7972       break;
7973
7974     Print("%04d: ", i);
7975
7976     for (j = 0; j < MAX_PLAYERS; j++)
7977     {
7978       if (tape->player_participates[j])
7979       {
7980         int action = tape->pos[i].action[j];
7981
7982         Print("%d:%02x ", j, action);
7983         Print("[%c%c%c%c|%c%c] - ",
7984               (action & JOY_LEFT ? '<' : ' '),
7985               (action & JOY_RIGHT ? '>' : ' '),
7986               (action & JOY_UP ? '^' : ' '),
7987               (action & JOY_DOWN ? 'v' : ' '),
7988               (action & JOY_BUTTON_1 ? '1' : ' '),
7989               (action & JOY_BUTTON_2 ? '2' : ' '));
7990       }
7991     }
7992
7993     Print("(%03d) ", tape->pos[i].delay);
7994     Print("[%05d]\n", tape_frame_counter);
7995
7996     tape_frame_counter += tape->pos[i].delay;
7997   }
7998
7999   PrintLine("-", 79);
8000 }
8001
8002
8003 /* ========================================================================= */
8004 /* score file functions                                                      */
8005 /* ========================================================================= */
8006
8007 void LoadScore(int nr)
8008 {
8009   int i;
8010   char *filename = getScoreFilename(nr);
8011   char cookie[MAX_LINE_LEN];
8012   char line[MAX_LINE_LEN];
8013   char *line_ptr;
8014   FILE *file;
8015
8016   /* always start with reliable default values */
8017   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8018   {
8019     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8020     highscore[i].Score = 0;
8021   }
8022
8023   if (!(file = fopen(filename, MODE_READ)))
8024     return;
8025
8026   /* check file identifier */
8027   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8028     cookie[0] = '\0';
8029   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8030     cookie[strlen(cookie) - 1] = '\0';
8031
8032   if (!checkCookieString(cookie, SCORE_COOKIE))
8033   {
8034     Error(ERR_WARN, "unknown format of score file '%s'", filename);
8035     fclose(file);
8036     return;
8037   }
8038
8039   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8040   {
8041     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8042       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8043     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8044       line[0] = '\0';
8045
8046     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8047       line[strlen(line) - 1] = '\0';
8048
8049     for (line_ptr = line; *line_ptr; line_ptr++)
8050     {
8051       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8052       {
8053         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8054         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8055         break;
8056       }
8057     }
8058   }
8059
8060   fclose(file);
8061 }
8062
8063 void SaveScore(int nr)
8064 {
8065   int i;
8066   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8067   char *filename = getScoreFilename(nr);
8068   FILE *file;
8069
8070   InitScoreDirectory(leveldir_current->subdir);
8071
8072   if (!(file = fopen(filename, MODE_WRITE)))
8073   {
8074     Error(ERR_WARN, "cannot save score for level %d", nr);
8075     return;
8076   }
8077
8078   fprintf(file, "%s\n\n", SCORE_COOKIE);
8079
8080   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8081     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8082
8083   fclose(file);
8084
8085   SetFilePermissions(filename, permissions);
8086 }
8087
8088
8089 /* ========================================================================= */
8090 /* setup file functions                                                      */
8091 /* ========================================================================= */
8092
8093 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8094
8095 /* global setup */
8096 #define SETUP_TOKEN_PLAYER_NAME                 0
8097 #define SETUP_TOKEN_SOUND                       1
8098 #define SETUP_TOKEN_SOUND_LOOPS                 2
8099 #define SETUP_TOKEN_SOUND_MUSIC                 3
8100 #define SETUP_TOKEN_SOUND_SIMPLE                4
8101 #define SETUP_TOKEN_TOONS                       5
8102 #define SETUP_TOKEN_SCROLL_DELAY                6
8103 #define SETUP_TOKEN_SCROLL_DELAY_VALUE          7
8104 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE        8
8105 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY      9
8106 #define SETUP_TOKEN_FADE_SCREENS                10
8107 #define SETUP_TOKEN_AUTORECORD                  11
8108 #define SETUP_TOKEN_SHOW_TITLESCREEN            12
8109 #define SETUP_TOKEN_QUICK_DOORS                 13
8110 #define SETUP_TOKEN_TEAM_MODE                   14
8111 #define SETUP_TOKEN_HANDICAP                    15
8112 #define SETUP_TOKEN_SKIP_LEVELS                 16
8113 #define SETUP_TOKEN_INCREMENT_LEVELS            17
8114 #define SETUP_TOKEN_TIME_LIMIT                  18
8115 #define SETUP_TOKEN_FULLSCREEN                  19
8116 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT      20
8117 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY      21
8118 #define SETUP_TOKEN_SCREEN_RENDERING_MODE       22
8119 #define SETUP_TOKEN_ASK_ON_ESCAPE               23
8120 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        24
8121 #define SETUP_TOKEN_QUICK_SWITCH                25
8122 #define SETUP_TOKEN_INPUT_ON_FOCUS              26
8123 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         27
8124 #define SETUP_TOKEN_GAME_FRAME_DELAY            28
8125 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS     29
8126 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS         30
8127 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS       31
8128 #define SETUP_TOKEN_GRAPHICS_SET                32
8129 #define SETUP_TOKEN_SOUNDS_SET                  33
8130 #define SETUP_TOKEN_MUSIC_SET                   34
8131 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     35
8132 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       36
8133 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        37
8134 #define SETUP_TOKEN_VOLUME_SIMPLE               38
8135 #define SETUP_TOKEN_VOLUME_LOOPS                39
8136 #define SETUP_TOKEN_VOLUME_MUSIC                40
8137 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE          41
8138 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE         42
8139 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE         43
8140
8141 #define NUM_GLOBAL_SETUP_TOKENS                 44
8142
8143 /* editor setup */
8144 #define SETUP_TOKEN_EDITOR_EL_CLASSIC           0
8145 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            1
8146 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      2
8147 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           3
8148 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         4
8149 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   5
8150
8151 #define NUM_EDITOR_SETUP_TOKENS                 6
8152
8153 /* editor cascade setup */
8154 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
8155 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
8156 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
8157 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
8158 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
8159 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
8160 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
8161 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
8162 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
8163 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT    9
8164 #define SETUP_TOKEN_EDITOR_CASCADE_CE           10
8165 #define SETUP_TOKEN_EDITOR_CASCADE_GE           11
8166 #define SETUP_TOKEN_EDITOR_CASCADE_REF          12
8167 #define SETUP_TOKEN_EDITOR_CASCADE_USER         13
8168 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      14
8169
8170 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         15
8171
8172 /* shortcut setup */
8173 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
8174 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
8175 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
8176 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
8177 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
8178 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
8179 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
8180 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
8181 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT         8
8182 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA         9
8183 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP          10
8184 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE         11
8185 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD        12
8186 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY          13
8187 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE       14
8188 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS        15
8189 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC        16
8190 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT          17
8191 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT         18
8192 #define SETUP_TOKEN_SHORTCUT_SNAP_UP            19
8193 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN          20
8194
8195 #define NUM_SHORTCUT_SETUP_TOKENS               21
8196
8197 /* player setup */
8198 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
8199 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
8200 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
8201 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
8202 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
8203 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
8204 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
8205 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
8206 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
8207 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
8208 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
8209 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
8210 #define SETUP_TOKEN_PLAYER_KEY_UP               12
8211 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
8212 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
8213 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
8214
8215 #define NUM_PLAYER_SETUP_TOKENS                 16
8216
8217 /* system setup */
8218 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER      0
8219 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      1
8220 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  2
8221
8222 #define NUM_SYSTEM_SETUP_TOKENS                 3
8223
8224 /* internal setup */
8225 #define SETUP_TOKEN_INT_PROGRAM_TITLE           0
8226 #define SETUP_TOKEN_INT_PROGRAM_VERSION         1
8227 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR          2
8228 #define SETUP_TOKEN_INT_PROGRAM_EMAIL           3
8229 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE         4
8230 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT       5
8231 #define SETUP_TOKEN_INT_PROGRAM_COMPANY         6
8232 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE       7
8233 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET    8
8234 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET      9
8235 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET       10
8236 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE  11
8237 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE    12
8238 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE     13
8239 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES    14
8240 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8241 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE   16
8242 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH    17
8243 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT   18
8244
8245 #define NUM_INTERNAL_SETUP_TOKENS               19
8246
8247 /* debug setup */
8248 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0         0
8249 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1         1
8250 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2         2
8251 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3         3
8252 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4         4
8253 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5         5
8254 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6         6
8255 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7         7
8256 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8         8
8257 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9         9
8258 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0     10
8259 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1     11
8260 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2     12
8261 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3     13
8262 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4     14
8263 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5     15
8264 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6     16
8265 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7     17
8266 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8     18
8267 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9     19
8268 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8269 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8270 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8271
8272 #define NUM_DEBUG_SETUP_TOKENS                  23
8273
8274 /* options setup */
8275 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
8276
8277 #define NUM_OPTIONS_SETUP_TOKENS                1
8278
8279
8280 static struct SetupInfo si;
8281 static struct SetupEditorInfo sei;
8282 static struct SetupEditorCascadeInfo seci;
8283 static struct SetupShortcutInfo ssi;
8284 static struct SetupInputInfo sii;
8285 static struct SetupSystemInfo syi;
8286 static struct SetupInternalInfo sxi;
8287 static struct SetupDebugInfo sdi;
8288 static struct OptionInfo soi;
8289
8290 static struct TokenInfo global_setup_tokens[] =
8291 {
8292   { TYPE_STRING, &si.player_name,             "player_name"             },
8293   { TYPE_SWITCH, &si.sound,                   "sound"                   },
8294   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
8295   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
8296   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
8297   { TYPE_SWITCH, &si.toons,                   "toons"                   },
8298   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
8299   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
8300   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"    },
8301   { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory"  },
8302   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
8303   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
8304   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
8305   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
8306   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
8307   { TYPE_SWITCH, &si.handicap,                "handicap"                },
8308   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
8309   { TYPE_SWITCH, &si.increment_levels,        "increment_levels"        },
8310   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
8311   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
8312   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
8313   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
8314   { TYPE_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"   },
8315   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
8316   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
8317   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
8318   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
8319   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
8320   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
8321   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8322   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
8323   { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"   },
8324   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
8325   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
8326   { TYPE_STRING, &si.music_set,               "music_set"               },
8327   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8328   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
8329   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
8330   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
8331   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
8332   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
8333   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
8334   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
8335   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
8336 };
8337
8338 static struct TokenInfo editor_setup_tokens[] =
8339 {
8340   { TYPE_SWITCH, &sei.el_classic,       "editor.el_classic"             },
8341   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
8342   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
8343   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
8344   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
8345   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
8346 };
8347
8348 static struct TokenInfo editor_cascade_setup_tokens[] =
8349 {
8350   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
8351   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
8352   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
8353   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
8354   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
8355   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
8356   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
8357   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
8358   { TYPE_SWITCH, &seci.el_mm,           "editor.cascade.el_mm"          },
8359   { TYPE_SWITCH, &seci.el_df,           "editor.cascade.el_df"          },
8360   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
8361   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
8362   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
8363   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
8364   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
8365   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
8366   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
8367 };
8368
8369 static struct TokenInfo shortcut_setup_tokens[] =
8370 {
8371   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
8372   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
8373   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
8374   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
8375   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
8376   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
8377   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
8378   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
8379   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
8380   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
8381   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
8382   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
8383   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
8384   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
8385   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
8386   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
8387   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
8388   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
8389   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
8390   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
8391   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
8392 };
8393
8394 static struct TokenInfo player_setup_tokens[] =
8395 {
8396   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
8397   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
8398   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
8399   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
8400   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
8401   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
8402   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
8403   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
8404   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
8405   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
8406   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
8407   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
8408   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
8409   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
8410   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
8411   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
8412 };
8413
8414 static struct TokenInfo system_setup_tokens[] =
8415 {
8416   { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"     },
8417   { TYPE_STRING,  &syi.sdl_audiodriver,    "system.sdl_audiodriver"     },
8418   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8419 };
8420
8421 static struct TokenInfo internal_setup_tokens[] =
8422 {
8423   { TYPE_STRING, &sxi.program_title,            "program_title"         },
8424   { TYPE_STRING, &sxi.program_version,          "program_version"       },
8425   { TYPE_STRING, &sxi.program_author,           "program_author"        },
8426   { TYPE_STRING, &sxi.program_email,            "program_email"         },
8427   { TYPE_STRING, &sxi.program_website,          "program_website"       },
8428   { TYPE_STRING, &sxi.program_copyright,        "program_copyright"     },
8429   { TYPE_STRING, &sxi.program_company,          "program_company"       },
8430   { TYPE_STRING, &sxi.program_icon_file,        "program_icon_file"     },
8431   { TYPE_STRING, &sxi.default_graphics_set,     "default_graphics_set"  },
8432   { TYPE_STRING, &sxi.default_sounds_set,       "default_sounds_set"    },
8433   { TYPE_STRING, &sxi.default_music_set,        "default_music_set"     },
8434   { TYPE_STRING, &sxi.fallback_graphics_file,   "fallback_graphics_file"},
8435   { TYPE_STRING, &sxi.fallback_sounds_file,     "fallback_sounds_file"  },
8436   { TYPE_STRING, &sxi.fallback_music_file,      "fallback_music_file"   },
8437   { TYPE_STRING, &sxi.default_level_series,     "default_level_series"  },
8438   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8439   { TYPE_BOOLEAN,&sxi.show_scaling_in_title,    "show_scaling_in_title" },
8440   { TYPE_INTEGER,&sxi.default_window_width,     "default_window_width"  },
8441   { TYPE_INTEGER,&sxi.default_window_height,    "default_window_height" },
8442 };
8443
8444 static struct TokenInfo debug_setup_tokens[] =
8445 {
8446   { TYPE_INTEGER, &sdi.frame_delay[0],          "debug.frame_delay_0"   },
8447   { TYPE_INTEGER, &sdi.frame_delay[1],          "debug.frame_delay_1"   },
8448   { TYPE_INTEGER, &sdi.frame_delay[2],          "debug.frame_delay_2"   },
8449   { TYPE_INTEGER, &sdi.frame_delay[3],          "debug.frame_delay_3"   },
8450   { TYPE_INTEGER, &sdi.frame_delay[4],          "debug.frame_delay_4"   },
8451   { TYPE_INTEGER, &sdi.frame_delay[5],          "debug.frame_delay_5"   },
8452   { TYPE_INTEGER, &sdi.frame_delay[6],          "debug.frame_delay_6"   },
8453   { TYPE_INTEGER, &sdi.frame_delay[7],          "debug.frame_delay_7"   },
8454   { TYPE_INTEGER, &sdi.frame_delay[8],          "debug.frame_delay_8"   },
8455   { TYPE_INTEGER, &sdi.frame_delay[9],          "debug.frame_delay_9"   },
8456   { TYPE_KEY_X11, &sdi.frame_delay_key[0],      "debug.key.frame_delay_0" },
8457   { TYPE_KEY_X11, &sdi.frame_delay_key[1],      "debug.key.frame_delay_1" },
8458   { TYPE_KEY_X11, &sdi.frame_delay_key[2],      "debug.key.frame_delay_2" },
8459   { TYPE_KEY_X11, &sdi.frame_delay_key[3],      "debug.key.frame_delay_3" },
8460   { TYPE_KEY_X11, &sdi.frame_delay_key[4],      "debug.key.frame_delay_4" },
8461   { TYPE_KEY_X11, &sdi.frame_delay_key[5],      "debug.key.frame_delay_5" },
8462   { TYPE_KEY_X11, &sdi.frame_delay_key[6],      "debug.key.frame_delay_6" },
8463   { TYPE_KEY_X11, &sdi.frame_delay_key[7],      "debug.key.frame_delay_7" },
8464   { TYPE_KEY_X11, &sdi.frame_delay_key[8],      "debug.key.frame_delay_8" },
8465   { TYPE_KEY_X11, &sdi.frame_delay_key[9],      "debug.key.frame_delay_9" },
8466   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8467   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
8468   { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8469 };
8470
8471 static struct TokenInfo options_setup_tokens[] =
8472 {
8473   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
8474 };
8475
8476 static char *get_corrected_login_name(char *login_name)
8477 {
8478   /* needed because player name must be a fixed length string */
8479   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8480
8481   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8482   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8483
8484   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
8485     if (strchr(login_name_new, ' '))
8486       *strchr(login_name_new, ' ') = '\0';
8487
8488   return login_name_new;
8489 }
8490
8491 static void setSetupInfoToDefaults(struct SetupInfo *si)
8492 {
8493   int i;
8494
8495   si->player_name = get_corrected_login_name(getLoginName());
8496
8497   si->sound = TRUE;
8498   si->sound_loops = TRUE;
8499   si->sound_music = TRUE;
8500   si->sound_simple = TRUE;
8501   si->toons = TRUE;
8502   si->scroll_delay = TRUE;
8503   si->scroll_delay_value = STD_SCROLL_DELAY;
8504   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8505   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8506   si->fade_screens = TRUE;
8507   si->autorecord = TRUE;
8508   si->show_titlescreen = TRUE;
8509   si->quick_doors = FALSE;
8510   si->team_mode = FALSE;
8511   si->handicap = TRUE;
8512   si->skip_levels = TRUE;
8513   si->increment_levels = TRUE;
8514   si->time_limit = TRUE;
8515   si->fullscreen = FALSE;
8516   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8517   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8518   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8519   si->ask_on_escape = TRUE;
8520   si->ask_on_escape_editor = TRUE;
8521   si->quick_switch = FALSE;
8522   si->input_on_focus = FALSE;
8523   si->prefer_aga_graphics = TRUE;
8524   si->game_frame_delay = GAME_FRAME_DELAY;
8525   si->sp_show_border_elements = FALSE;
8526   si->small_game_graphics = FALSE;
8527   si->show_snapshot_buttons = FALSE;
8528
8529   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8530   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8531   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8532
8533   si->override_level_graphics = FALSE;
8534   si->override_level_sounds = FALSE;
8535   si->override_level_music = FALSE;
8536
8537   si->volume_simple = 100;              /* percent */
8538   si->volume_loops = 100;               /* percent */
8539   si->volume_music = 100;               /* percent */
8540
8541   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8542   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
8543   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
8544
8545   si->editor.el_boulderdash             = TRUE;
8546   si->editor.el_emerald_mine            = TRUE;
8547   si->editor.el_emerald_mine_club       = TRUE;
8548   si->editor.el_more                    = TRUE;
8549   si->editor.el_sokoban                 = TRUE;
8550   si->editor.el_supaplex                = TRUE;
8551   si->editor.el_diamond_caves           = TRUE;
8552   si->editor.el_dx_boulderdash          = TRUE;
8553
8554   si->editor.el_mirror_magic            = TRUE;
8555   si->editor.el_deflektor               = TRUE;
8556
8557   si->editor.el_chars                   = TRUE;
8558   si->editor.el_steel_chars             = TRUE;
8559
8560   si->editor.el_classic                 = TRUE;
8561   si->editor.el_custom                  = TRUE;
8562
8563   si->editor.el_user_defined            = FALSE;
8564   si->editor.el_dynamic                 = TRUE;
8565
8566   si->editor.el_headlines               = TRUE;
8567
8568   si->editor.show_element_token         = FALSE;
8569
8570   si->editor.use_template_for_new_levels = TRUE;
8571
8572   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
8573   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
8574   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
8575
8576   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
8577   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
8578   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
8579   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
8580   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8581
8582   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
8583   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
8584   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
8585   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
8586   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
8587   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
8588
8589   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
8590   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
8591   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
8592
8593   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
8594   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
8595   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
8596   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
8597
8598   for (i = 0; i < MAX_PLAYERS; i++)
8599   {
8600     si->input[i].use_joystick = FALSE;
8601     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8602     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
8603     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8604     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
8605     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
8606     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8607     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
8608     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
8609     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
8610     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
8611     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8612     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
8613     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
8614     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
8615     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
8616   }
8617
8618   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8619   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8620   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8621
8622   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
8623   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
8624   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
8625   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
8626   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
8627   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8628   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
8629
8630   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8631
8632   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8633   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8634   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8635
8636   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8637   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
8638   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
8639
8640   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8641   si->internal.choose_from_top_leveldir = FALSE;
8642   si->internal.show_scaling_in_title = TRUE;
8643
8644   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
8645   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8646
8647   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8648   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8649   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8650   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8651   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8652   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8653   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8654   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8655   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8656   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8657
8658   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8659   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8660   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8661   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8662   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8663   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8664   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8665   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8666   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8667   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8668
8669   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8670   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
8671
8672   si->debug.show_frames_per_second = FALSE;
8673
8674   si->options.verbose = FALSE;
8675
8676 #if defined(PLATFORM_ANDROID)
8677   si->fullscreen = TRUE;
8678 #endif
8679 }
8680
8681 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8682 {
8683   si->editor_cascade.el_bd              = TRUE;
8684   si->editor_cascade.el_em              = TRUE;
8685   si->editor_cascade.el_emc             = TRUE;
8686   si->editor_cascade.el_rnd             = TRUE;
8687   si->editor_cascade.el_sb              = TRUE;
8688   si->editor_cascade.el_sp              = TRUE;
8689   si->editor_cascade.el_dc              = TRUE;
8690   si->editor_cascade.el_dx              = TRUE;
8691
8692   si->editor_cascade.el_mm              = TRUE;
8693   si->editor_cascade.el_df              = TRUE;
8694
8695   si->editor_cascade.el_chars           = FALSE;
8696   si->editor_cascade.el_steel_chars     = FALSE;
8697   si->editor_cascade.el_ce              = FALSE;
8698   si->editor_cascade.el_ge              = FALSE;
8699   si->editor_cascade.el_ref             = FALSE;
8700   si->editor_cascade.el_user            = FALSE;
8701   si->editor_cascade.el_dynamic         = FALSE;
8702 }
8703
8704 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
8705
8706 static char *getHideSetupToken(void *setup_value)
8707 {
8708   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8709
8710   if (setup_value != NULL)
8711     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8712
8713   return hide_setup_token;
8714 }
8715
8716 static void setHideSetupEntry(void *setup_value_raw)
8717 {
8718   /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8719   void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8720
8721   char *hide_setup_token = getHideSetupToken(setup_value);
8722
8723   if (setup_value != NULL)
8724     setHashEntry(hide_setup_hash, hide_setup_token, "");
8725 }
8726
8727 boolean hideSetupEntry(void *setup_value)
8728 {
8729   char *hide_setup_token = getHideSetupToken(setup_value);
8730
8731   return (setup_value != NULL &&
8732           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8733 }
8734
8735 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8736                                       struct TokenInfo *token_info,
8737                                       int token_nr, char *token_text)
8738 {
8739   char *token_hide_text = getStringCat2(token_text, ".hide");
8740   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8741
8742   /* set the value of this setup option in the setup option structure */
8743   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8744
8745   /* check if this setup option should be hidden in the setup menu */
8746   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8747     setHideSetupEntry(token_info[token_nr].value);
8748 }
8749
8750 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8751                                       struct TokenInfo *token_info,
8752                                       int token_nr)
8753 {
8754   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8755                             token_info[token_nr].text);
8756 }
8757
8758 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8759 {
8760   int i, pnr;
8761
8762   if (!setup_file_hash)
8763     return;
8764
8765   if (hide_setup_hash == NULL)
8766     hide_setup_hash = newSetupFileHash();
8767
8768   /* global setup */
8769   si = setup;
8770   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8771     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8772   setup = si;
8773
8774   /* editor setup */
8775   sei = setup.editor;
8776   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8777     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8778   setup.editor = sei;
8779
8780   /* shortcut setup */
8781   ssi = setup.shortcut;
8782   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8783     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8784   setup.shortcut = ssi;
8785
8786   /* player setup */
8787   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8788   {
8789     char prefix[30];
8790
8791     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8792
8793     sii = setup.input[pnr];
8794     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8795     {
8796       char full_token[100];
8797
8798       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8799       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8800                                 full_token);
8801     }
8802     setup.input[pnr] = sii;
8803   }
8804
8805   /* system setup */
8806   syi = setup.system;
8807   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8808     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8809   setup.system = syi;
8810
8811   /* internal setup */
8812   sxi = setup.internal;
8813   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8814     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8815   setup.internal = sxi;
8816
8817   /* debug setup */
8818   sdi = setup.debug;
8819   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8820     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8821   setup.debug = sdi;
8822
8823   /* options setup */
8824   soi = setup.options;
8825   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8826     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8827   setup.options = soi;
8828 }
8829
8830 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8831 {
8832   int i;
8833
8834   if (!setup_file_hash)
8835     return;
8836
8837   /* editor cascade setup */
8838   seci = setup.editor_cascade;
8839   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8840     setSetupInfo(editor_cascade_setup_tokens, i,
8841                  getHashEntry(setup_file_hash,
8842                               editor_cascade_setup_tokens[i].text));
8843   setup.editor_cascade = seci;
8844 }
8845
8846 void LoadSetupFromFilename(char *filename)
8847 {
8848   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8849
8850   if (setup_file_hash)
8851   {
8852     decodeSetupFileHash(setup_file_hash);
8853
8854     freeSetupFileHash(setup_file_hash);
8855   }
8856   else
8857   {
8858     Error(ERR_DEBUG, "using default setup values");
8859   }
8860 }
8861
8862 static void LoadSetup_SpecialPostProcessing()
8863 {
8864   char *player_name_new;
8865
8866   /* needed to work around problems with fixed length strings */
8867   player_name_new = get_corrected_login_name(setup.player_name);
8868   free(setup.player_name);
8869   setup.player_name = player_name_new;
8870
8871   /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8872   if (setup.scroll_delay == FALSE)
8873   {
8874     setup.scroll_delay_value = MIN_SCROLL_DELAY;
8875     setup.scroll_delay = TRUE;                  /* now always "on" */
8876   }
8877
8878   /* make sure that scroll delay value stays inside valid range */
8879   setup.scroll_delay_value =
8880     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8881 }
8882
8883 void LoadSetup()
8884 {
8885   char *filename;
8886
8887   /* always start with reliable default values */
8888   setSetupInfoToDefaults(&setup);
8889
8890   /* try to load setup values from default setup file */
8891   filename = getDefaultSetupFilename();
8892
8893   if (fileExists(filename))
8894     LoadSetupFromFilename(filename);
8895
8896   /* try to load setup values from user setup file */
8897   filename = getSetupFilename();
8898
8899   LoadSetupFromFilename(filename);
8900
8901   LoadSetup_SpecialPostProcessing();
8902 }
8903
8904 void LoadSetup_EditorCascade()
8905 {
8906   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8907   SetupFileHash *setup_file_hash = NULL;
8908
8909   /* always start with reliable default values */
8910   setSetupInfoToDefaults_EditorCascade(&setup);
8911
8912   setup_file_hash = loadSetupFileHash(filename);
8913
8914   if (setup_file_hash)
8915   {
8916     decodeSetupFileHash_EditorCascade(setup_file_hash);
8917
8918     freeSetupFileHash(setup_file_hash);
8919   }
8920
8921   free(filename);
8922 }
8923
8924 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
8925                                            char *mapping_line)
8926 {
8927   char mapping_guid[MAX_LINE_LEN];
8928   char *mapping_start, *mapping_end;
8929
8930   // get GUID from game controller mapping line: copy complete line
8931   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
8932   mapping_guid[MAX_LINE_LEN - 1] = '\0';
8933
8934   // get GUID from game controller mapping line: cut after GUID part
8935   mapping_start = strchr(mapping_guid, ',');
8936   if (mapping_start != NULL)
8937     *mapping_start = '\0';
8938
8939   // cut newline from game controller mapping line
8940   mapping_end = strchr(mapping_line, '\n');
8941   if (mapping_end != NULL)
8942     *mapping_end = '\0';
8943
8944   // add mapping entry to game controller mappings hash
8945   setHashEntry(mappings_hash, mapping_guid, mapping_line);
8946 }
8947
8948 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
8949                                                  char *filename)
8950 {
8951   FILE *file;
8952
8953   if (!(file = fopen(filename, MODE_READ)))
8954   {
8955     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
8956
8957     return;
8958   }
8959
8960   while (!feof(file))
8961   {
8962     char line[MAX_LINE_LEN];
8963
8964     if (!fgets(line, MAX_LINE_LEN, file))
8965       break;
8966
8967     addGameControllerMappingToHash(mappings_hash, line);
8968   }
8969
8970   fclose(file);
8971 }
8972
8973 void SaveSetup()
8974 {
8975   char *filename = getSetupFilename();
8976   FILE *file;
8977   int i, pnr;
8978
8979   InitUserDataDirectory();
8980
8981   if (!(file = fopen(filename, MODE_WRITE)))
8982   {
8983     Error(ERR_WARN, "cannot write setup file '%s'", filename);
8984     return;
8985   }
8986
8987   fprintFileHeader(file, SETUP_FILENAME);
8988
8989   /* global setup */
8990   si = setup;
8991   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8992   {
8993     /* just to make things nicer :) */
8994     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8995         i == SETUP_TOKEN_GRAPHICS_SET ||
8996         i == SETUP_TOKEN_VOLUME_SIMPLE ||
8997         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8998       fprintf(file, "\n");
8999
9000     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9001   }
9002
9003   /* editor setup */
9004   sei = setup.editor;
9005   fprintf(file, "\n");
9006   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9007     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9008
9009   /* shortcut setup */
9010   ssi = setup.shortcut;
9011   fprintf(file, "\n");
9012   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9013     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9014
9015   /* player setup */
9016   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9017   {
9018     char prefix[30];
9019
9020     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9021     fprintf(file, "\n");
9022
9023     sii = setup.input[pnr];
9024     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9025       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9026   }
9027
9028   /* system setup */
9029   syi = setup.system;
9030   fprintf(file, "\n");
9031   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9032     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9033
9034   /* internal setup */
9035   /* (internal setup values not saved to user setup file) */
9036
9037   /* debug setup */
9038   sdi = setup.debug;
9039   fprintf(file, "\n");
9040   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9041     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9042
9043   /* options setup */
9044   soi = setup.options;
9045   fprintf(file, "\n");
9046   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9047     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9048
9049   fclose(file);
9050
9051   SetFilePermissions(filename, PERMS_PRIVATE);
9052 }
9053
9054 void SaveSetup_EditorCascade()
9055 {
9056   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9057   FILE *file;
9058   int i;
9059
9060   InitUserDataDirectory();
9061
9062   if (!(file = fopen(filename, MODE_WRITE)))
9063   {
9064     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9065     free(filename);
9066     return;
9067   }
9068
9069   fprintFileHeader(file, EDITORCASCADE_FILENAME);
9070
9071   seci = setup.editor_cascade;
9072   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9073     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9074
9075   fclose(file);
9076
9077   SetFilePermissions(filename, PERMS_PRIVATE);
9078
9079   free(filename);
9080 }
9081
9082 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9083                                                   char *filename)
9084 {
9085   FILE *file;
9086
9087   if (!(file = fopen(filename, MODE_WRITE)))
9088   {
9089     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9090
9091     return;
9092   }
9093
9094   BEGIN_HASH_ITERATION(mappings_hash, itr)
9095   {
9096     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9097   }
9098   END_HASH_ITERATION(mappings_hash, itr)
9099
9100   fclose(file);
9101 }
9102
9103 void SaveSetup_AddGameControllerMapping(char *mapping)
9104 {
9105   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9106   SetupFileHash *mappings_hash = newSetupFileHash();
9107
9108   InitUserDataDirectory();
9109
9110   // load existing personal game controller mappings
9111   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9112
9113   // add new mapping to personal game controller mappings
9114   addGameControllerMappingToHash(mappings_hash, mapping);
9115
9116   // save updated personal game controller mappings
9117   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9118
9119   freeSetupFileHash(mappings_hash);
9120   free(filename);
9121 }
9122
9123 void LoadCustomElementDescriptions()
9124 {
9125   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9126   SetupFileHash *setup_file_hash;
9127   int i;
9128
9129   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9130   {
9131     if (element_info[i].custom_description != NULL)
9132     {
9133       free(element_info[i].custom_description);
9134       element_info[i].custom_description = NULL;
9135     }
9136   }
9137
9138   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9139     return;
9140
9141   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9142   {
9143     char *token = getStringCat2(element_info[i].token_name, ".name");
9144     char *value = getHashEntry(setup_file_hash, token);
9145
9146     if (value != NULL)
9147       element_info[i].custom_description = getStringCopy(value);
9148
9149     free(token);
9150   }
9151
9152   freeSetupFileHash(setup_file_hash);
9153 }
9154
9155 static int getElementFromToken(char *token)
9156 {
9157   char *value = getHashEntry(element_token_hash, token);
9158
9159   if (value != NULL)
9160     return atoi(value);
9161
9162   Error(ERR_WARN, "unknown element token '%s'", token);
9163
9164   return EL_UNDEFINED;
9165 }
9166
9167 static int get_token_parameter_value(char *token, char *value_raw)
9168 {
9169   char *suffix;
9170
9171   if (token == NULL || value_raw == NULL)
9172     return ARG_UNDEFINED_VALUE;
9173
9174   suffix = strrchr(token, '.');
9175   if (suffix == NULL)
9176     suffix = token;
9177
9178   if (strEqual(suffix, ".element"))
9179     return getElementFromToken(value_raw);
9180
9181   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9182   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9183 }
9184
9185 void InitMenuDesignSettings_Static()
9186 {
9187   int i;
9188
9189   /* always start with reliable default values from static default config */
9190   for (i = 0; image_config_vars[i].token != NULL; i++)
9191   {
9192     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9193
9194     if (value != NULL)
9195       *image_config_vars[i].value =
9196         get_token_parameter_value(image_config_vars[i].token, value);
9197   }
9198 }
9199
9200 static void InitMenuDesignSettings_SpecialPreProcessing()
9201 {
9202   int i;
9203
9204   /* the following initializes hierarchical values from static configuration */
9205
9206   /* special case: initialize "ARG_DEFAULT" values in static default config */
9207   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9208   titlescreen_initial_first_default.fade_mode  =
9209     title_initial_first_default.fade_mode;
9210   titlescreen_initial_first_default.fade_delay =
9211     title_initial_first_default.fade_delay;
9212   titlescreen_initial_first_default.post_delay =
9213     title_initial_first_default.post_delay;
9214   titlescreen_initial_first_default.auto_delay =
9215     title_initial_first_default.auto_delay;
9216   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
9217   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9218   titlescreen_first_default.post_delay = title_first_default.post_delay;
9219   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9220   titlemessage_initial_first_default.fade_mode  =
9221     title_initial_first_default.fade_mode;
9222   titlemessage_initial_first_default.fade_delay =
9223     title_initial_first_default.fade_delay;
9224   titlemessage_initial_first_default.post_delay =
9225     title_initial_first_default.post_delay;
9226   titlemessage_initial_first_default.auto_delay =
9227     title_initial_first_default.auto_delay;
9228   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
9229   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9230   titlemessage_first_default.post_delay = title_first_default.post_delay;
9231   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9232
9233   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
9234   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9235   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9236   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9237   titlescreen_default.fade_mode  = title_default.fade_mode;
9238   titlescreen_default.fade_delay = title_default.fade_delay;
9239   titlescreen_default.post_delay = title_default.post_delay;
9240   titlescreen_default.auto_delay = title_default.auto_delay;
9241   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
9242   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9243   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9244   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9245   titlemessage_default.fade_mode  = title_default.fade_mode;
9246   titlemessage_default.fade_delay = title_default.fade_delay;
9247   titlemessage_default.post_delay = title_default.post_delay;
9248   titlemessage_default.auto_delay = title_default.auto_delay;
9249
9250   /* special case: initialize "ARG_DEFAULT" values in static default config */
9251   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9252   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9253   {
9254     titlescreen_initial_first[i] = titlescreen_initial_first_default;
9255     titlescreen_first[i] = titlescreen_first_default;
9256     titlemessage_initial_first[i] = titlemessage_initial_first_default;
9257     titlemessage_first[i] = titlemessage_first_default;
9258
9259     titlescreen_initial[i] = titlescreen_initial_default;
9260     titlescreen[i] = titlescreen_default;
9261     titlemessage_initial[i] = titlemessage_initial_default;
9262     titlemessage[i] = titlemessage_default;
9263   }
9264
9265   /* special case: initialize "ARG_DEFAULT" values in static default config */
9266   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9267   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9268   {
9269     if (i == GFX_SPECIAL_ARG_TITLE)     /* title values already initialized */
9270       continue;
9271
9272     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9273     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9274     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9275   }
9276
9277   /* special case: initialize "ARG_DEFAULT" values in static default config */
9278   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9279   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9280   {
9281     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9282     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9283     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9284
9285     if (i == GFX_SPECIAL_ARG_EDITOR)    /* editor values already initialized */
9286       continue;
9287
9288     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9289   }
9290 }
9291
9292 static void InitMenuDesignSettings_SpecialPostProcessing()
9293 {
9294   static struct
9295   {
9296     struct XY *dst, *src;
9297   }
9298   game_buttons_xy[] =
9299   {
9300     { &game.button.save,        &game.button.stop       },
9301     { &game.button.pause2,      &game.button.pause      },
9302     { &game.button.load,        &game.button.play       },
9303     { &game.button.undo,        &game.button.stop       },
9304     { &game.button.redo,        &game.button.play       },
9305
9306     { NULL,                     NULL                    }
9307   };
9308   int i;
9309
9310   /* special case: initialize later added SETUP list size from LEVELS value */
9311   if (menu.list_size[GAME_MODE_SETUP] == -1)
9312     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9313
9314   /* set default position for snapshot buttons to stop/pause/play buttons */
9315   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9316     if ((*game_buttons_xy[i].dst).x == -1 &&
9317         (*game_buttons_xy[i].dst).y == -1)
9318       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9319 }
9320
9321 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9322 {
9323   static struct
9324   {
9325     struct XYTileSize *dst, *src;
9326     int graphic;
9327   }
9328   editor_buttons_xy[] =
9329   {
9330     {
9331       &editor.button.element_left,      &editor.palette.element_left,
9332       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9333     },
9334     {
9335       &editor.button.element_middle,    &editor.palette.element_middle,
9336       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9337     },
9338     {
9339       &editor.button.element_right,     &editor.palette.element_right,
9340       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9341     },
9342
9343     { NULL,                     NULL                    }
9344   };
9345   int i;
9346
9347   /* set default position for element buttons to element graphics */
9348   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9349   {
9350     if ((*editor_buttons_xy[i].dst).x == -1 &&
9351         (*editor_buttons_xy[i].dst).y == -1)
9352     {
9353       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9354
9355       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9356
9357       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9358     }
9359   }
9360 }
9361
9362 static void LoadMenuDesignSettingsFromFilename(char *filename)
9363 {
9364   static struct TitleFadingInfo tfi;
9365   static struct TitleMessageInfo tmi;
9366   static struct TokenInfo title_tokens[] =
9367   {
9368     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
9369     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
9370     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
9371     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
9372
9373     { -1,               NULL,                   NULL                    }
9374   };
9375   static struct TokenInfo titlemessage_tokens[] =
9376   {
9377     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9378     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9379     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9380     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9381     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9382     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9383     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9384     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9385     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9386     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9387     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9388     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9389     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9390     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9391     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9392     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9393     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9394
9395     { -1,               NULL,                   NULL                    }
9396   };
9397   static struct
9398   {
9399     struct TitleFadingInfo *info;
9400     char *text;
9401   }
9402   title_info[] =
9403   {
9404     /* initialize first titles from "enter screen" definitions, if defined */
9405     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
9406     { &title_first_default,             "menu.enter_screen.TITLE"       },
9407
9408     /* initialize title screens from "next screen" definitions, if defined */
9409     { &title_initial_default,           "menu.next_screen.TITLE"        },
9410     { &title_default,                   "menu.next_screen.TITLE"        },
9411
9412     { NULL,                             NULL                            }
9413   };
9414   static struct
9415   {
9416     struct TitleMessageInfo *array;
9417     char *text;
9418   }
9419   titlemessage_arrays[] =
9420   {
9421     /* initialize first titles from "enter screen" definitions, if defined */
9422     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
9423     { titlescreen_first,                "menu.enter_screen.TITLE"       },
9424     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
9425     { titlemessage_first,               "menu.enter_screen.TITLE"       },
9426
9427     /* initialize titles from "next screen" definitions, if defined */
9428     { titlescreen_initial,              "menu.next_screen.TITLE"        },
9429     { titlescreen,                      "menu.next_screen.TITLE"        },
9430     { titlemessage_initial,             "menu.next_screen.TITLE"        },
9431     { titlemessage,                     "menu.next_screen.TITLE"        },
9432
9433     /* overwrite titles with title definitions, if defined */
9434     { titlescreen_initial_first,        "[title_initial]"               },
9435     { titlescreen_first,                "[title]"                       },
9436     { titlemessage_initial_first,       "[title_initial]"               },
9437     { titlemessage_first,               "[title]"                       },
9438
9439     { titlescreen_initial,              "[title_initial]"               },
9440     { titlescreen,                      "[title]"                       },
9441     { titlemessage_initial,             "[title_initial]"               },
9442     { titlemessage,                     "[title]"                       },
9443
9444     /* overwrite titles with title screen/message definitions, if defined */
9445     { titlescreen_initial_first,        "[titlescreen_initial]"         },
9446     { titlescreen_first,                "[titlescreen]"                 },
9447     { titlemessage_initial_first,       "[titlemessage_initial]"        },
9448     { titlemessage_first,               "[titlemessage]"                },
9449
9450     { titlescreen_initial,              "[titlescreen_initial]"         },
9451     { titlescreen,                      "[titlescreen]"                 },
9452     { titlemessage_initial,             "[titlemessage_initial]"        },
9453     { titlemessage,                     "[titlemessage]"                },
9454
9455     { NULL,                             NULL                            }
9456   };
9457   SetupFileHash *setup_file_hash;
9458   int i, j, k;
9459
9460   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9461     return;
9462
9463   /* the following initializes hierarchical values from dynamic configuration */
9464
9465   /* special case: initialize with default values that may be overwritten */
9466   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9467   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9468   {
9469     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9470     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9471     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9472
9473     if (value_1 != NULL)
9474       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9475     if (value_2 != NULL)
9476       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9477     if (value_3 != NULL)
9478       menu.list_size[i] = get_integer_from_string(value_3);
9479   }
9480
9481   /* special case: initialize with default values that may be overwritten */
9482   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9483   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9484   {
9485     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9486     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9487
9488     if (value_1 != NULL)
9489       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9490     if (value_2 != NULL)
9491       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9492
9493     if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9494     {
9495       char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9496
9497       if (value_1 != NULL)
9498         menu.list_size_info[i] = get_integer_from_string(value_1);
9499     }
9500   }
9501
9502   /* special case: initialize with default values that may be overwritten */
9503   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9504   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9505   {
9506     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9507     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9508
9509     if (value_1 != NULL)
9510       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9511     if (value_2 != NULL)
9512       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9513   }
9514
9515   /* special case: initialize with default values that may be overwritten */
9516   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9517   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9518   {
9519     char *token_1 = "menu.enter_screen.fade_mode";
9520     char *token_2 = "menu.enter_screen.fade_delay";
9521     char *token_3 = "menu.enter_screen.post_delay";
9522     char *token_4 = "menu.leave_screen.fade_mode";
9523     char *token_5 = "menu.leave_screen.fade_delay";
9524     char *token_6 = "menu.leave_screen.post_delay";
9525     char *token_7 = "menu.next_screen.fade_mode";
9526     char *token_8 = "menu.next_screen.fade_delay";
9527     char *token_9 = "menu.next_screen.post_delay";
9528     char *value_1 = getHashEntry(setup_file_hash, token_1);
9529     char *value_2 = getHashEntry(setup_file_hash, token_2);
9530     char *value_3 = getHashEntry(setup_file_hash, token_3);
9531     char *value_4 = getHashEntry(setup_file_hash, token_4);
9532     char *value_5 = getHashEntry(setup_file_hash, token_5);
9533     char *value_6 = getHashEntry(setup_file_hash, token_6);
9534     char *value_7 = getHashEntry(setup_file_hash, token_7);
9535     char *value_8 = getHashEntry(setup_file_hash, token_8);
9536     char *value_9 = getHashEntry(setup_file_hash, token_9);
9537
9538     if (value_1 != NULL)
9539       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9540                                                                  value_1);
9541     if (value_2 != NULL)
9542       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9543                                                                   value_2);
9544     if (value_3 != NULL)
9545       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9546                                                                   value_3);
9547     if (value_4 != NULL)
9548       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9549                                                                  value_4);
9550     if (value_5 != NULL)
9551       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9552                                                                   value_5);
9553     if (value_6 != NULL)
9554       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9555                                                                   value_6);
9556     if (value_7 != NULL)
9557       menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9558                                                                 value_7);
9559     if (value_8 != NULL)
9560       menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9561                                                                  value_8);
9562     if (value_9 != NULL)
9563       menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9564                                                                  value_9);
9565   }
9566
9567   /* special case: initialize with default values that may be overwritten */
9568   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9569   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9570   {
9571     char *token_w1 = "viewport.window.width";
9572     char *token_w2 = "viewport.window.height";
9573     char *token_01 = "viewport.playfield.x";
9574     char *token_02 = "viewport.playfield.y";
9575     char *token_03 = "viewport.playfield.width";
9576     char *token_04 = "viewport.playfield.height";
9577     char *token_05 = "viewport.playfield.border_size";
9578     char *token_06 = "viewport.door_1.x";
9579     char *token_07 = "viewport.door_1.y";
9580     char *token_08 = "viewport.door_1.width";
9581     char *token_09 = "viewport.door_1.height";
9582     char *token_10 = "viewport.door_1.border_size";
9583     char *token_11 = "viewport.door_2.x";
9584     char *token_12 = "viewport.door_2.y";
9585     char *token_13 = "viewport.door_2.width";
9586     char *token_14 = "viewport.door_2.height";
9587     char *token_15 = "viewport.door_2.border_size";
9588     char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9589     char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9590     char *value_01 = getHashEntry(setup_file_hash, token_01);
9591     char *value_02 = getHashEntry(setup_file_hash, token_02);
9592     char *value_03 = getHashEntry(setup_file_hash, token_03);
9593     char *value_04 = getHashEntry(setup_file_hash, token_04);
9594     char *value_05 = getHashEntry(setup_file_hash, token_05);
9595     char *value_06 = getHashEntry(setup_file_hash, token_06);
9596     char *value_07 = getHashEntry(setup_file_hash, token_07);
9597     char *value_08 = getHashEntry(setup_file_hash, token_08);
9598     char *value_09 = getHashEntry(setup_file_hash, token_09);
9599     char *value_10 = getHashEntry(setup_file_hash, token_10);
9600     char *value_11 = getHashEntry(setup_file_hash, token_11);
9601     char *value_12 = getHashEntry(setup_file_hash, token_12);
9602     char *value_13 = getHashEntry(setup_file_hash, token_13);
9603     char *value_14 = getHashEntry(setup_file_hash, token_14);
9604     char *value_15 = getHashEntry(setup_file_hash, token_15);
9605
9606     if (value_w1 != NULL)
9607       viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9608     if (value_w2 != NULL)
9609       viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9610     if (value_01 != NULL)
9611       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9612     if (value_02 != NULL)
9613       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9614     if (value_03 != NULL)
9615       viewport.playfield[i].width = get_token_parameter_value(token_03,
9616                                                               value_03);
9617     if (value_04 != NULL)
9618       viewport.playfield[i].height = get_token_parameter_value(token_04,
9619                                                                value_04);
9620     if (value_05 != NULL)
9621       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9622                                                                     value_05);
9623     if (value_06 != NULL)
9624       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9625     if (value_07 != NULL)
9626       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9627     if (value_08 != NULL)
9628       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9629     if (value_09 != NULL)
9630       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9631     if (value_10 != NULL)
9632       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9633                                                                  value_10);
9634     if (value_11 != NULL)
9635       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9636     if (value_12 != NULL)
9637       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9638     if (value_13 != NULL)
9639       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9640     if (value_14 != NULL)
9641       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9642     if (value_15 != NULL)
9643       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9644                                                                  value_15);
9645   }
9646
9647   /* special case: initialize with default values that may be overwritten */
9648   /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9649   for (i = 0; title_info[i].info != NULL; i++)
9650   {
9651     struct TitleFadingInfo *info = title_info[i].info;
9652     char *base_token = title_info[i].text;
9653
9654     for (j = 0; title_tokens[j].type != -1; j++)
9655     {
9656       char *token = getStringCat2(base_token, title_tokens[j].text);
9657       char *value = getHashEntry(setup_file_hash, token);
9658
9659       if (value != NULL)
9660       {
9661         int parameter_value = get_token_parameter_value(token, value);
9662
9663         tfi = *info;
9664
9665         *(int *)title_tokens[j].value = (int)parameter_value;
9666
9667         *info = tfi;
9668       }
9669
9670       free(token);
9671     }
9672   }
9673
9674   /* special case: initialize with default values that may be overwritten */
9675   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9676   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9677   {
9678     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9679     char *base_token = titlemessage_arrays[i].text;
9680
9681     for (j = 0; titlemessage_tokens[j].type != -1; j++)
9682     {
9683       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9684       char *value = getHashEntry(setup_file_hash, token);
9685
9686       if (value != NULL)
9687       {
9688         int parameter_value = get_token_parameter_value(token, value);
9689
9690         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9691         {
9692           tmi = array[k];
9693
9694           if (titlemessage_tokens[j].type == TYPE_INTEGER)
9695             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
9696           else
9697             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9698
9699           array[k] = tmi;
9700         }
9701       }
9702
9703       free(token);
9704     }
9705   }
9706
9707   /* read (and overwrite with) values that may be specified in config file */
9708   for (i = 0; image_config_vars[i].token != NULL; i++)
9709   {
9710     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9711
9712     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9713     if (value != NULL && !strEqual(value, ARG_DEFAULT))
9714       *image_config_vars[i].value =
9715         get_token_parameter_value(image_config_vars[i].token, value);
9716   }
9717
9718   freeSetupFileHash(setup_file_hash);
9719 }
9720
9721 void LoadMenuDesignSettings()
9722 {
9723   char *filename_base = UNDEFINED_FILENAME, *filename_local;
9724
9725   InitMenuDesignSettings_Static();
9726   InitMenuDesignSettings_SpecialPreProcessing();
9727
9728   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9729   {
9730     /* first look for special settings configured in level series config */
9731     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9732
9733     if (fileExists(filename_base))
9734       LoadMenuDesignSettingsFromFilename(filename_base);
9735   }
9736
9737   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9738
9739   if (filename_local != NULL && !strEqual(filename_base, filename_local))
9740     LoadMenuDesignSettingsFromFilename(filename_local);
9741
9742   InitMenuDesignSettings_SpecialPostProcessing();
9743 }
9744
9745 void LoadMenuDesignSettings_AfterGraphics()
9746 {
9747   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9748 }
9749
9750 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9751 {
9752   char *filename = getEditorSetupFilename();
9753   SetupFileList *setup_file_list, *list;
9754   SetupFileHash *element_hash;
9755   int num_unknown_tokens = 0;
9756   int i;
9757
9758   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9759     return;
9760
9761   element_hash = newSetupFileHash();
9762
9763   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9764     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9765
9766   /* determined size may be larger than needed (due to unknown elements) */
9767   *num_elements = 0;
9768   for (list = setup_file_list; list != NULL; list = list->next)
9769     (*num_elements)++;
9770
9771   /* add space for up to 3 more elements for padding that may be needed */
9772   *num_elements += 3;
9773
9774   /* free memory for old list of elements, if needed */
9775   checked_free(*elements);
9776
9777   /* allocate memory for new list of elements */
9778   *elements = checked_malloc(*num_elements * sizeof(int));
9779
9780   *num_elements = 0;
9781   for (list = setup_file_list; list != NULL; list = list->next)
9782   {
9783     char *value = getHashEntry(element_hash, list->token);
9784
9785     if (value == NULL)          /* try to find obsolete token mapping */
9786     {
9787       char *mapped_token = get_mapped_token(list->token);
9788
9789       if (mapped_token != NULL)
9790       {
9791         value = getHashEntry(element_hash, mapped_token);
9792
9793         free(mapped_token);
9794       }
9795     }
9796
9797     if (value != NULL)
9798     {
9799       (*elements)[(*num_elements)++] = atoi(value);
9800     }
9801     else
9802     {
9803       if (num_unknown_tokens == 0)
9804       {
9805         Error(ERR_INFO_LINE, "-");
9806         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9807         Error(ERR_INFO, "- config file: '%s'", filename);
9808
9809         num_unknown_tokens++;
9810       }
9811
9812       Error(ERR_INFO, "- token: '%s'", list->token);
9813     }
9814   }
9815
9816   if (num_unknown_tokens > 0)
9817     Error(ERR_INFO_LINE, "-");
9818
9819   while (*num_elements % 4)     /* pad with empty elements, if needed */
9820     (*elements)[(*num_elements)++] = EL_EMPTY;
9821
9822   freeSetupFileList(setup_file_list);
9823   freeSetupFileHash(element_hash);
9824
9825 #if 0
9826   for (i = 0; i < *num_elements; i++)
9827     printf("editor: element '%s' [%d]\n",
9828            element_info[(*elements)[i]].token_name, (*elements)[i]);
9829 #endif
9830 }
9831
9832 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9833                                                      boolean is_sound)
9834 {
9835   SetupFileHash *setup_file_hash = NULL;
9836   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9837   char *filename_music, *filename_prefix, *filename_info;
9838   struct
9839   {
9840     char *token;
9841     char **value_ptr;
9842   }
9843   token_to_value_ptr[] =
9844   {
9845     { "title_header",   &tmp_music_file_info.title_header       },
9846     { "artist_header",  &tmp_music_file_info.artist_header      },
9847     { "album_header",   &tmp_music_file_info.album_header       },
9848     { "year_header",    &tmp_music_file_info.year_header        },
9849
9850     { "title",          &tmp_music_file_info.title              },
9851     { "artist",         &tmp_music_file_info.artist             },
9852     { "album",          &tmp_music_file_info.album              },
9853     { "year",           &tmp_music_file_info.year               },
9854
9855     { NULL,             NULL                                    },
9856   };
9857   int i;
9858
9859   filename_music = (is_sound ? getCustomSoundFilename(basename) :
9860                     getCustomMusicFilename(basename));
9861
9862   if (filename_music == NULL)
9863     return NULL;
9864
9865   /* ---------- try to replace file extension ---------- */
9866
9867   filename_prefix = getStringCopy(filename_music);
9868   if (strrchr(filename_prefix, '.') != NULL)
9869     *strrchr(filename_prefix, '.') = '\0';
9870   filename_info = getStringCat2(filename_prefix, ".txt");
9871
9872   if (fileExists(filename_info))
9873     setup_file_hash = loadSetupFileHash(filename_info);
9874
9875   free(filename_prefix);
9876   free(filename_info);
9877
9878   if (setup_file_hash == NULL)
9879   {
9880     /* ---------- try to add file extension ---------- */
9881
9882     filename_prefix = getStringCopy(filename_music);
9883     filename_info = getStringCat2(filename_prefix, ".txt");
9884
9885     if (fileExists(filename_info))
9886       setup_file_hash = loadSetupFileHash(filename_info);
9887
9888     free(filename_prefix);
9889     free(filename_info);
9890   }
9891
9892   if (setup_file_hash == NULL)
9893     return NULL;
9894
9895   /* ---------- music file info found ---------- */
9896
9897   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9898
9899   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9900   {
9901     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9902
9903     *token_to_value_ptr[i].value_ptr =
9904       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9905   }
9906
9907   tmp_music_file_info.basename = getStringCopy(basename);
9908   tmp_music_file_info.music = music;
9909   tmp_music_file_info.is_sound = is_sound;
9910
9911   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9912   *new_music_file_info = tmp_music_file_info;
9913
9914   return new_music_file_info;
9915 }
9916
9917 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9918 {
9919   return get_music_file_info_ext(basename, music, FALSE);
9920 }
9921
9922 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9923 {
9924   return get_music_file_info_ext(basename, sound, TRUE);
9925 }
9926
9927 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9928                                      char *basename, boolean is_sound)
9929 {
9930   for (; list != NULL; list = list->next)
9931     if (list->is_sound == is_sound && strEqual(list->basename, basename))
9932       return TRUE;
9933
9934   return FALSE;
9935 }
9936
9937 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9938 {
9939   return music_info_listed_ext(list, basename, FALSE);
9940 }
9941
9942 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9943 {
9944   return music_info_listed_ext(list, basename, TRUE);
9945 }
9946
9947 void LoadMusicInfo()
9948 {
9949   char *music_directory = getCustomMusicDirectory();
9950   int num_music = getMusicListSize();
9951   int num_music_noconf = 0;
9952   int num_sounds = getSoundListSize();
9953   Directory *dir;
9954   DirectoryEntry *dir_entry;
9955   struct FileInfo *music, *sound;
9956   struct MusicFileInfo *next, **new;
9957   int i;
9958
9959   while (music_file_info != NULL)
9960   {
9961     next = music_file_info->next;
9962
9963     checked_free(music_file_info->basename);
9964
9965     checked_free(music_file_info->title_header);
9966     checked_free(music_file_info->artist_header);
9967     checked_free(music_file_info->album_header);
9968     checked_free(music_file_info->year_header);
9969
9970     checked_free(music_file_info->title);
9971     checked_free(music_file_info->artist);
9972     checked_free(music_file_info->album);
9973     checked_free(music_file_info->year);
9974
9975     free(music_file_info);
9976
9977     music_file_info = next;
9978   }
9979
9980   new = &music_file_info;
9981
9982   for (i = 0; i < num_music; i++)
9983   {
9984     music = getMusicListEntry(i);
9985
9986     if (music->filename == NULL)
9987       continue;
9988
9989     if (strEqual(music->filename, UNDEFINED_FILENAME))
9990       continue;
9991
9992     /* a configured file may be not recognized as music */
9993     if (!FileIsMusic(music->filename))
9994       continue;
9995
9996     if (!music_info_listed(music_file_info, music->filename))
9997     {
9998       *new = get_music_file_info(music->filename, i);
9999
10000       if (*new != NULL)
10001         new = &(*new)->next;
10002     }
10003   }
10004
10005   if ((dir = openDirectory(music_directory)) == NULL)
10006   {
10007     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10008     return;
10009   }
10010
10011   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
10012   {
10013     char *basename = dir_entry->basename;
10014     boolean music_already_used = FALSE;
10015     int i;
10016
10017     /* skip all music files that are configured in music config file */
10018     for (i = 0; i < num_music; i++)
10019     {
10020       music = getMusicListEntry(i);
10021
10022       if (music->filename == NULL)
10023         continue;
10024
10025       if (strEqual(basename, music->filename))
10026       {
10027         music_already_used = TRUE;
10028         break;
10029       }
10030     }
10031
10032     if (music_already_used)
10033       continue;
10034
10035     if (!FileIsMusic(dir_entry->filename))
10036       continue;
10037
10038     if (!music_info_listed(music_file_info, basename))
10039     {
10040       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10041
10042       if (*new != NULL)
10043         new = &(*new)->next;
10044     }
10045
10046     num_music_noconf++;
10047   }
10048
10049   closeDirectory(dir);
10050
10051   for (i = 0; i < num_sounds; i++)
10052   {
10053     sound = getSoundListEntry(i);
10054
10055     if (sound->filename == NULL)
10056       continue;
10057
10058     if (strEqual(sound->filename, UNDEFINED_FILENAME))
10059       continue;
10060
10061     /* a configured file may be not recognized as sound */
10062     if (!FileIsSound(sound->filename))
10063       continue;
10064
10065     if (!sound_info_listed(music_file_info, sound->filename))
10066     {
10067       *new = get_sound_file_info(sound->filename, i);
10068       if (*new != NULL)
10069         new = &(*new)->next;
10070     }
10071   }
10072 }
10073
10074 void add_helpanim_entry(int element, int action, int direction, int delay,
10075                         int *num_list_entries)
10076 {
10077   struct HelpAnimInfo *new_list_entry;
10078   (*num_list_entries)++;
10079
10080   helpanim_info =
10081     checked_realloc(helpanim_info,
10082                     *num_list_entries * sizeof(struct HelpAnimInfo));
10083   new_list_entry = &helpanim_info[*num_list_entries - 1];
10084
10085   new_list_entry->element = element;
10086   new_list_entry->action = action;
10087   new_list_entry->direction = direction;
10088   new_list_entry->delay = delay;
10089 }
10090
10091 void print_unknown_token(char *filename, char *token, int token_nr)
10092 {
10093   if (token_nr == 0)
10094   {
10095     Error(ERR_INFO_LINE, "-");
10096     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10097     Error(ERR_INFO, "- config file: '%s'", filename);
10098   }
10099
10100   Error(ERR_INFO, "- token: '%s'", token);
10101 }
10102
10103 void print_unknown_token_end(int token_nr)
10104 {
10105   if (token_nr > 0)
10106     Error(ERR_INFO_LINE, "-");
10107 }
10108
10109 void LoadHelpAnimInfo()
10110 {
10111   char *filename = getHelpAnimFilename();
10112   SetupFileList *setup_file_list = NULL, *list;
10113   SetupFileHash *element_hash, *action_hash, *direction_hash;
10114   int num_list_entries = 0;
10115   int num_unknown_tokens = 0;
10116   int i;
10117
10118   if (fileExists(filename))
10119     setup_file_list = loadSetupFileList(filename);
10120
10121   if (setup_file_list == NULL)
10122   {
10123     /* use reliable default values from static configuration */
10124     SetupFileList *insert_ptr;
10125
10126     insert_ptr = setup_file_list =
10127       newSetupFileList(helpanim_config[0].token,
10128                        helpanim_config[0].value);
10129
10130     for (i = 1; helpanim_config[i].token; i++)
10131       insert_ptr = addListEntry(insert_ptr,
10132                                 helpanim_config[i].token,
10133                                 helpanim_config[i].value);
10134   }
10135
10136   element_hash   = newSetupFileHash();
10137   action_hash    = newSetupFileHash();
10138   direction_hash = newSetupFileHash();
10139
10140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10141     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10142
10143   for (i = 0; i < NUM_ACTIONS; i++)
10144     setHashEntry(action_hash, element_action_info[i].suffix,
10145                  i_to_a(element_action_info[i].value));
10146
10147   /* do not store direction index (bit) here, but direction value! */
10148   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10149     setHashEntry(direction_hash, element_direction_info[i].suffix,
10150                  i_to_a(1 << element_direction_info[i].value));
10151
10152   for (list = setup_file_list; list != NULL; list = list->next)
10153   {
10154     char *element_token, *action_token, *direction_token;
10155     char *element_value, *action_value, *direction_value;
10156     int delay = atoi(list->value);
10157
10158     if (strEqual(list->token, "end"))
10159     {
10160       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10161
10162       continue;
10163     }
10164
10165     /* first try to break element into element/action/direction parts;
10166        if this does not work, also accept combined "element[.act][.dir]"
10167        elements (like "dynamite.active"), which are unique elements */
10168
10169     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
10170     {
10171       element_value = getHashEntry(element_hash, list->token);
10172       if (element_value != NULL)        /* element found */
10173         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10174                            &num_list_entries);
10175       else
10176       {
10177         /* no further suffixes found -- this is not an element */
10178         print_unknown_token(filename, list->token, num_unknown_tokens++);
10179       }
10180
10181       continue;
10182     }
10183
10184     /* token has format "<prefix>.<something>" */
10185
10186     action_token = strchr(list->token, '.');    /* suffix may be action ... */
10187     direction_token = action_token;             /* ... or direction */
10188
10189     element_token = getStringCopy(list->token);
10190     *strchr(element_token, '.') = '\0';
10191
10192     element_value = getHashEntry(element_hash, element_token);
10193
10194     if (element_value == NULL)          /* this is no element */
10195     {
10196       element_value = getHashEntry(element_hash, list->token);
10197       if (element_value != NULL)        /* combined element found */
10198         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10199                            &num_list_entries);
10200       else
10201         print_unknown_token(filename, list->token, num_unknown_tokens++);
10202
10203       free(element_token);
10204
10205       continue;
10206     }
10207
10208     action_value = getHashEntry(action_hash, action_token);
10209
10210     if (action_value != NULL)           /* action found */
10211     {
10212       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10213                     &num_list_entries);
10214
10215       free(element_token);
10216
10217       continue;
10218     }
10219
10220     direction_value = getHashEntry(direction_hash, direction_token);
10221
10222     if (direction_value != NULL)        /* direction found */
10223     {
10224       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10225                          &num_list_entries);
10226
10227       free(element_token);
10228
10229       continue;
10230     }
10231
10232     if (strchr(action_token + 1, '.') == NULL)
10233     {
10234       /* no further suffixes found -- this is not an action nor direction */
10235
10236       element_value = getHashEntry(element_hash, list->token);
10237       if (element_value != NULL)        /* combined element found */
10238         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10239                            &num_list_entries);
10240       else
10241         print_unknown_token(filename, list->token, num_unknown_tokens++);
10242
10243       free(element_token);
10244
10245       continue;
10246     }
10247
10248     /* token has format "<prefix>.<suffix>.<something>" */
10249
10250     direction_token = strchr(action_token + 1, '.');
10251
10252     action_token = getStringCopy(action_token);
10253     *strchr(action_token + 1, '.') = '\0';
10254
10255     action_value = getHashEntry(action_hash, action_token);
10256
10257     if (action_value == NULL)           /* this is no action */
10258     {
10259       element_value = getHashEntry(element_hash, list->token);
10260       if (element_value != NULL)        /* combined element found */
10261         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10262                            &num_list_entries);
10263       else
10264         print_unknown_token(filename, list->token, num_unknown_tokens++);
10265
10266       free(element_token);
10267       free(action_token);
10268
10269       continue;
10270     }
10271
10272     direction_value = getHashEntry(direction_hash, direction_token);
10273
10274     if (direction_value != NULL)        /* direction found */
10275     {
10276       add_helpanim_entry(atoi(element_value), atoi(action_value),
10277                          atoi(direction_value), delay, &num_list_entries);
10278
10279       free(element_token);
10280       free(action_token);
10281
10282       continue;
10283     }
10284
10285     /* this is no direction */
10286
10287     element_value = getHashEntry(element_hash, list->token);
10288     if (element_value != NULL)          /* combined element found */
10289       add_helpanim_entry(atoi(element_value), -1, -1, delay,
10290                          &num_list_entries);
10291     else
10292       print_unknown_token(filename, list->token, num_unknown_tokens++);
10293
10294     free(element_token);
10295     free(action_token);
10296   }
10297
10298   print_unknown_token_end(num_unknown_tokens);
10299
10300   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10301   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
10302
10303   freeSetupFileList(setup_file_list);
10304   freeSetupFileHash(element_hash);
10305   freeSetupFileHash(action_hash);
10306   freeSetupFileHash(direction_hash);
10307
10308 #if 0
10309   for (i = 0; i < num_list_entries; i++)
10310     printf("::: '%s': %d, %d, %d => %d\n",
10311            EL_NAME(helpanim_info[i].element),
10312            helpanim_info[i].element,
10313            helpanim_info[i].action,
10314            helpanim_info[i].direction,
10315            helpanim_info[i].delay);
10316 #endif
10317 }
10318
10319 void LoadHelpTextInfo()
10320 {
10321   char *filename = getHelpTextFilename();
10322   int i;
10323
10324   if (helptext_info != NULL)
10325   {
10326     freeSetupFileHash(helptext_info);
10327     helptext_info = NULL;
10328   }
10329
10330   if (fileExists(filename))
10331     helptext_info = loadSetupFileHash(filename);
10332
10333   if (helptext_info == NULL)
10334   {
10335     /* use reliable default values from static configuration */
10336     helptext_info = newSetupFileHash();
10337
10338     for (i = 0; helptext_config[i].token; i++)
10339       setHashEntry(helptext_info,
10340                    helptext_config[i].token,
10341                    helptext_config[i].value);
10342   }
10343
10344 #if 0
10345   BEGIN_HASH_ITERATION(helptext_info, itr)
10346   {
10347     printf("::: '%s' => '%s'\n",
10348            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10349   }
10350   END_HASH_ITERATION(hash, itr)
10351 #endif
10352 }
10353
10354
10355 /* ------------------------------------------------------------------------- */
10356 /* convert levels                                                            */
10357 /* ------------------------------------------------------------------------- */
10358
10359 #define MAX_NUM_CONVERT_LEVELS          1000
10360
10361 void ConvertLevels()
10362 {
10363   static LevelDirTree *convert_leveldir = NULL;
10364   static int convert_level_nr = -1;
10365   static int num_levels_handled = 0;
10366   static int num_levels_converted = 0;
10367   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10368   int i;
10369
10370   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10371                                                global.convert_leveldir);
10372
10373   if (convert_leveldir == NULL)
10374     Error(ERR_EXIT, "no such level identifier: '%s'",
10375           global.convert_leveldir);
10376
10377   leveldir_current = convert_leveldir;
10378
10379   if (global.convert_level_nr != -1)
10380   {
10381     convert_leveldir->first_level = global.convert_level_nr;
10382     convert_leveldir->last_level  = global.convert_level_nr;
10383   }
10384
10385   convert_level_nr = convert_leveldir->first_level;
10386
10387   PrintLine("=", 79);
10388   Print("Converting levels\n");
10389   PrintLine("-", 79);
10390   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10391   Print("Level series name:       '%s'\n", convert_leveldir->name);
10392   Print("Level series author:     '%s'\n", convert_leveldir->author);
10393   Print("Number of levels:        %d\n",   convert_leveldir->levels);
10394   PrintLine("=", 79);
10395   Print("\n");
10396
10397   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10398     levels_failed[i] = FALSE;
10399
10400   while (convert_level_nr <= convert_leveldir->last_level)
10401   {
10402     char *level_filename;
10403     boolean new_level;
10404
10405     level_nr = convert_level_nr++;
10406
10407     Print("Level %03d: ", level_nr);
10408
10409     LoadLevel(level_nr);
10410     if (level.no_level_file || level.no_valid_file)
10411     {
10412       Print("(no level)\n");
10413       continue;
10414     }
10415
10416     Print("converting level ... ");
10417
10418     level_filename = getDefaultLevelFilename(level_nr);
10419     new_level = !fileExists(level_filename);
10420
10421     if (new_level)
10422     {
10423       SaveLevel(level_nr);
10424
10425       num_levels_converted++;
10426
10427       Print("converted.\n");
10428     }
10429     else
10430     {
10431       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10432         levels_failed[level_nr] = TRUE;
10433
10434       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10435     }
10436
10437     num_levels_handled++;
10438   }
10439
10440   Print("\n");
10441   PrintLine("=", 79);
10442   Print("Number of levels handled: %d\n", num_levels_handled);
10443   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10444          (num_levels_handled ?
10445           num_levels_converted * 100 / num_levels_handled : 0));
10446   PrintLine("-", 79);
10447   Print("Summary (for automatic parsing by scripts):\n");
10448   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10449          convert_leveldir->identifier, num_levels_converted,
10450          num_levels_handled,
10451          (num_levels_handled ?
10452           num_levels_converted * 100 / num_levels_handled : 0));
10453
10454   if (num_levels_handled != num_levels_converted)
10455   {
10456     Print(", FAILED:");
10457     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10458       if (levels_failed[i])
10459         Print(" %03d", i);
10460   }
10461
10462   Print("\n");
10463   PrintLine("=", 79);
10464
10465   CloseAllAndExit(0);
10466 }
10467
10468
10469 /* ------------------------------------------------------------------------- */
10470 /* create and save images for use in level sketches (raw BMP format)         */
10471 /* ------------------------------------------------------------------------- */
10472
10473 void CreateLevelSketchImages()
10474 {
10475 #if defined(TARGET_SDL)
10476   Bitmap *bitmap1;
10477   Bitmap *bitmap2;
10478   int i;
10479
10480   InitElementPropertiesGfxElement();
10481
10482   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10483   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10484
10485   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10486   {
10487     Bitmap *src_bitmap;
10488     int src_x, src_y;
10489     int element = getMappedElement(i);
10490     int graphic = el2edimg(element);
10491     char basename1[16];
10492     char basename2[16];
10493     char *filename1;
10494     char *filename2;
10495
10496     sprintf(basename1, "%03d.bmp", i);
10497     sprintf(basename2, "%03ds.bmp", i);
10498
10499     filename1 = getPath2(global.create_images_dir, basename1);
10500     filename2 = getPath2(global.create_images_dir, basename2);
10501
10502     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10503     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10504                0, 0);
10505
10506     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10507       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10508
10509     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10510     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10511
10512     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10513       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10514
10515     free(filename1);
10516     free(filename2);
10517
10518     if (options.debug)
10519       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10520   }
10521
10522   FreeBitmap(bitmap1);
10523   FreeBitmap(bitmap2);
10524
10525   if (options.debug)
10526     printf("\n");
10527
10528   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10529
10530   CloseAllAndExit(0);
10531 #endif
10532 }
10533
10534
10535 /* ------------------------------------------------------------------------- */
10536 /* create and save images for custom and group elements (raw BMP format)     */
10537 /* ------------------------------------------------------------------------- */
10538
10539 void CreateCustomElementImages(char *directory)
10540 {
10541 #if defined(TARGET_SDL)
10542   char *src_basename = "RocksCE-template.ilbm";
10543   char *dst_basename = "RocksCE.bmp";
10544   char *src_filename = getPath2(directory, src_basename);
10545   char *dst_filename = getPath2(directory, dst_basename);
10546   Bitmap *src_bitmap;
10547   Bitmap *bitmap;
10548   int yoffset_ce = 0;
10549   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10550   int i;
10551
10552   SDLInitVideoDisplay();
10553
10554   ReCreateBitmap(&backbuffer, video.width, video.height);
10555
10556   src_bitmap = LoadImage(src_filename);
10557
10558   bitmap = CreateBitmap(TILEX * 16 * 2,
10559                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10560                         DEFAULT_DEPTH);
10561
10562   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10563   {
10564     int x = i % 16;
10565     int y = i / 16;
10566     int ii = i + 1;
10567     int j;
10568
10569     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10570                TILEX * x, TILEY * y + yoffset_ce);
10571
10572     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10573                TILEX, TILEY,
10574                TILEX * x + TILEX * 16,
10575                TILEY * y + yoffset_ce);
10576
10577     for (j = 2; j >= 0; j--)
10578     {
10579       int c = ii % 10;
10580
10581       BlitBitmap(src_bitmap, bitmap,
10582                  TILEX + c * 7, 0, 6, 10,
10583                  TILEX * x + 6 + j * 7,
10584                  TILEY * y + 11 + yoffset_ce);
10585
10586       BlitBitmap(src_bitmap, bitmap,
10587                  TILEX + c * 8, TILEY, 6, 10,
10588                  TILEX * 16 + TILEX * x + 6 + j * 8,
10589                  TILEY * y + 10 + yoffset_ce);
10590
10591       ii /= 10;
10592     }
10593   }
10594
10595   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10596   {
10597     int x = i % 16;
10598     int y = i / 16;
10599     int ii = i + 1;
10600     int j;
10601
10602     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10603                TILEX * x, TILEY * y + yoffset_ge);
10604
10605     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10606                TILEX, TILEY,
10607                TILEX * x + TILEX * 16,
10608                TILEY * y + yoffset_ge);
10609
10610     for (j = 1; j >= 0; j--)
10611     {
10612       int c = ii % 10;
10613
10614       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10615                  TILEX * x + 6 + j * 10,
10616                  TILEY * y + 11 + yoffset_ge);
10617
10618       BlitBitmap(src_bitmap, bitmap,
10619                  TILEX + c * 8, TILEY + 12, 6, 10,
10620                  TILEX * 16 + TILEX * x + 10 + j * 8,
10621                  TILEY * y + 10 + yoffset_ge);
10622
10623       ii /= 10;
10624     }
10625   }
10626
10627   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10628     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10629
10630   FreeBitmap(bitmap);
10631
10632   CloseAllAndExit(0);
10633 #endif
10634 }