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