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