rnd-20060509-2-src
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * files.c                                                  *
12 ***********************************************************/
13
14 #include <ctype.h>
15 #include <sys/stat.h>
16 #include <dirent.h>
17 #include <math.h>
18
19 #include "libgame/libgame.h"
20
21 #include "files.h"
22 #include "init.h"
23 #include "tools.h"
24 #include "tape.h"
25
26
27 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
28 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
29 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
30 #define FILE_VERS_CHUNK_SIZE    8       /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE       80      /* size of level file header  */
32 #define LEVEL_HEADER_UNUSED     0       /* unused level header bytes  */
33 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
35 #define LEVEL_CHUNK_CNT3_HEADER 16      /* size of level CNT3 header  */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10      /* unused CNT3 chunk bytes    */
37 #define LEVEL_CPART_CUS3_SIZE   134     /* size of CUS3 chunk part    */
38 #define LEVEL_CPART_CUS3_UNUSED 15      /* unused CUS3 bytes / part   */
39 #define LEVEL_CHUNK_GRP1_SIZE   74      /* size of level GRP1 chunk   */
40 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
41 #define TAPE_HEADER_UNUSED      3       /* unused tape header bytes   */
42
43 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
44 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
45 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
46
47 /* file identifier strings */
48 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
49 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
50 #define SCORE_COOKIE                    "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
51
52 /* values for "CONF" chunk */
53 #define CONF_MASK_1_BYTE                0x00
54 #define CONF_MASK_2_BYTE                0x40
55 #define CONF_MASK_4_BYTE                0x80
56 #define CONF_MASK_MULTI_BYTES           0xc0
57
58 #define CONF_MASK_BYTES                 0xc0
59 #define CONF_MASK_TOKEN                 0x3f
60
61 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
62 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
63 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
64 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
65
66 /* a sequence of configuration values can be terminated by this value */
67 #define CONF_LAST_ENTRY                 CONF_VALUE_1_BYTE(0)
68
69 /* these definitions are just for convenience of use and readability */
70 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
71 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
72 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
73 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
74
75 #define CONF_VALUE_INTEGER_1            CONF_VALUE_8_BIT(1)
76 #define CONF_VALUE_INTEGER_2            CONF_VALUE_8_BIT(2)
77 #define CONF_VALUE_INTEGER_3            CONF_VALUE_8_BIT(3)
78 #define CONF_VALUE_INTEGER_4            CONF_VALUE_8_BIT(4)
79 #define CONF_VALUE_INTEGER_5            CONF_VALUE_8_BIT(5)
80 #define CONF_VALUE_INTEGER_6            CONF_VALUE_8_BIT(6)
81 #define CONF_VALUE_INTEGER_7            CONF_VALUE_8_BIT(7)
82 #define CONF_VALUE_INTEGER_8            CONF_VALUE_8_BIT(8)
83 #define CONF_VALUE_BOOLEAN_1            CONF_VALUE_8_BIT(9)
84 #define CONF_VALUE_BOOLEAN_2            CONF_VALUE_8_BIT(10)
85 #define CONF_VALUE_BOOLEAN_3            CONF_VALUE_8_BIT(11)
86 #define CONF_VALUE_BOOLEAN_4            CONF_VALUE_8_BIT(12)
87 #define CONF_VALUE_BOOLEAN_5            CONF_VALUE_8_BIT(13)
88 #define CONF_VALUE_BOOLEAN_6            CONF_VALUE_8_BIT(14)
89 #define CONF_VALUE_BOOLEAN_7            CONF_VALUE_8_BIT(15)
90 #define CONF_VALUE_BOOLEAN_8            CONF_VALUE_8_BIT(16)
91
92 #define CONF_VALUE_ELEMENT_1            CONF_VALUE_16_BIT(1)
93 #define CONF_VALUE_ELEMENT_2            CONF_VALUE_16_BIT(2)
94 #define CONF_VALUE_ELEMENT_3            CONF_VALUE_16_BIT(3)
95 #define CONF_VALUE_ELEMENT_4            CONF_VALUE_16_BIT(4)
96 #define CONF_VALUE_ELEMENT_5            CONF_VALUE_16_BIT(5)
97 #define CONF_VALUE_ELEMENT_6            CONF_VALUE_16_BIT(6)
98 #define CONF_VALUE_ELEMENT_7            CONF_VALUE_16_BIT(7)
99 #define CONF_VALUE_ELEMENT_8            CONF_VALUE_16_BIT(8)
100
101 #if 0
102 #define CONF_VALUE_ELEMENTS             CONF_VALUE_BYTES(1)
103 #define CONF_VALUE_CONTENTS             CONF_VALUE_BYTES(2)
104 #endif
105
106 #if 0
107 #define CONF_VALUE_INTEGER(x)           ((x) >= CONF_VALUE_INTEGER_1 && \
108                                          (x) <= CONF_VALUE_INTEGER_8)
109
110 #define CONF_VALUE_BOOLEAN(x)           ((x) >= CONF_VALUE_BOOLEAN_1 && \
111                                          (x) <= CONF_VALUE_BOOLEAN_8)
112 #endif
113
114 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
115                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
116                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
117
118 #if 0
119 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
120 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
121 #define CONF_ELEMENT_NUM_BYTES          (2)
122
123 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == CONF_VALUE_ELEMENTS ?   \
124                                          CONF_ELEMENT_NUM_BYTES :       \
125                                          (t) == CONF_VALUE_CONTENTS ?   \
126                                          CONF_CONTENT_NUM_BYTES : 1)
127 #endif
128
129 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
130 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
131 #define CONF_ELEMENT_NUM_BYTES          (2)
132
133 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
134                                          (t) == TYPE_ELEMENT_LIST ?     \
135                                          CONF_ELEMENT_NUM_BYTES :       \
136                                          (t) == TYPE_CONTENT ||         \
137                                          (t) == TYPE_CONTENT_LIST ?     \
138                                          CONF_CONTENT_NUM_BYTES : 1)
139
140 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
141 #define CONF_ELEMENTS_ELEMENT(b,i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) |  \
142                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
143
144 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
145                                          (y) * 3 + (x))
146 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
147                                          CONF_ELEMENT_NUM_BYTES)
148 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
149                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
150
151 #if 0
152 static void LoadLevel_InitPlayfield(struct LevelInfo *, char *);
153 #endif
154
155 /* temporary variables used to store pointers to structure members */
156 static struct LevelInfo li;
157 static struct ElementInfo xx_ei;
158 static struct ElementChangeInfo xx_change;
159 static struct ElementGroupInfo xx_group;
160 static unsigned int xx_event_bits_0_31, xx_event_bits_32_63;
161 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
162 static int xx_default_description_length;
163 static int xx_num_contents;
164 static int xx_current_change_page;
165
166 struct ElementFileConfig
167 {
168   int element;                  /* element for which data is to be stored */
169   int data_type;                /* internal type of data */
170   int conf_type;                /* special type identifier stored in file */
171
172   /* (mandatory) */
173   void *value;                  /* variable that holds the data to be stored */
174   int default_value;            /* initial default value for this variable */
175
176   /* (optional) */
177   void *num_entities;           /* number of entities for multi-byte data */
178   int default_num_entities;     /* default number of entities for this data */
179   int max_num_entities;         /* maximal number of entities for this data */
180   char *default_string;         /* optional default string for string data */
181 };
182
183 static struct ElementFileConfig element_conf[] =
184 {
185   /* ---------- 1-byte values ---------------------------------------------- */
186
187   {
188     EL_EMC_ANDROID,
189     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
190     &li.android_move_time,              10
191   },
192   {
193     EL_EMC_ANDROID,
194     TYPE_INTEGER,                       CONF_VALUE_INTEGER_2,
195     &li.android_clone_time,             10
196   },
197   {
198     EL_EMC_LENSES,
199     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
200     &li.lenses_score,                   10
201   },
202   {
203     EL_EMC_LENSES,
204     TYPE_INTEGER,                       CONF_VALUE_INTEGER_2,
205     &li.lenses_time,                    10
206   },
207   {
208     EL_EMC_MAGNIFIER,
209     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
210     &li.magnify_score,                  10
211   },
212   {
213     EL_EMC_MAGNIFIER,
214     TYPE_INTEGER,                       CONF_VALUE_INTEGER_2,
215     &li.magnify_time,                   10
216   },
217   {
218     EL_ROBOT,
219     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
220     &li.slurp_score,                    10
221   },
222   {
223     EL_GAME_OF_LIFE,
224     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
225     &li.game_of_life[0],                2
226   },
227   {
228     EL_GAME_OF_LIFE,
229     TYPE_INTEGER,                       CONF_VALUE_INTEGER_2,
230     &li.game_of_life[1],                3
231   },
232   {
233     EL_GAME_OF_LIFE,
234     TYPE_INTEGER,                       CONF_VALUE_INTEGER_3,
235     &li.game_of_life[2],                3
236   },
237   {
238     EL_GAME_OF_LIFE,
239     TYPE_INTEGER,                       CONF_VALUE_INTEGER_4,
240     &li.game_of_life[3],                3
241   },
242   {
243     EL_BIOMAZE,
244     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
245     &li.biomaze[0],                     2
246   },
247   {
248     EL_BIOMAZE,
249     TYPE_INTEGER,                       CONF_VALUE_INTEGER_2,
250     &li.biomaze[1],                     3
251   },
252   {
253     EL_BIOMAZE,
254     TYPE_INTEGER,                       CONF_VALUE_INTEGER_3,
255     &li.biomaze[2],                     3
256   },
257   {
258     EL_BIOMAZE,
259     TYPE_INTEGER,                       CONF_VALUE_INTEGER_4,
260     &li.biomaze[3],                     3
261   },
262   {
263     EL_BALLOON,
264     TYPE_BITFIELD,                      CONF_VALUE_INTEGER_1,
265     &li.wind_direction_initial,         MV_NONE
266   },
267   {
268     EL_TIMEGATE_SWITCH,
269     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
270     &li.time_timegate,                  10
271   },
272   {
273     EL_LIGHT_SWITCH_ACTIVE,
274     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
275     &li.time_light,                     10
276   },
277   {
278     EL_SHIELD_NORMAL,
279     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
280     &li.shield_normal_time,             10
281   },
282   {
283     EL_SHIELD_DEADLY,
284     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
285     &li.shield_deadly_time,             10
286   },
287   {
288     EL_EXTRA_TIME,
289     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
290     &li.extra_time,                     10
291   },
292   {
293     EL_EXTRA_TIME,
294     TYPE_INTEGER,                       CONF_VALUE_INTEGER_2,
295     &li.extra_time_score,               10
296   },
297   {
298     EL_TIME_ORB_FULL,
299     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
300     &li.time_orb_time,                  10
301   },
302   {
303     EL_TIME_ORB_FULL,
304     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_1,
305     &li.use_time_orb_bug,               FALSE
306   },
307   {
308     EL_PLAYER_1,
309     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_1,
310     &li.block_snap_field,               TRUE
311   },
312   {
313     EL_PLAYER_1,
314     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_2,
315     &li.use_start_element[0],           FALSE
316   },
317   {
318     EL_PLAYER_2,
319     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_2,
320     &li.use_start_element[1],           FALSE
321   },
322   {
323     EL_PLAYER_3,
324     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_2,
325     &li.use_start_element[2],           FALSE
326   },
327   {
328     EL_PLAYER_4,
329     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_2,
330     &li.use_start_element[3],           FALSE
331   },
332   {
333     EL_PLAYER_1,
334     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_3,
335     &li.use_artwork_element[0],         FALSE
336   },
337   {
338     EL_PLAYER_2,
339     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_3,
340     &li.use_artwork_element[1],         FALSE
341   },
342   {
343     EL_PLAYER_3,
344     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_3,
345     &li.use_artwork_element[2],         FALSE
346   },
347   {
348     EL_PLAYER_4,
349     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_3,
350     &li.use_artwork_element[3],         FALSE
351   },
352   {
353     EL_PLAYER_1,
354     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_4,
355     &li.use_explosion_element[0],       FALSE
356   },
357   {
358     EL_PLAYER_2,
359     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_4,
360     &li.use_explosion_element[1],       FALSE
361   },
362   {
363     EL_PLAYER_3,
364     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_4,
365     &li.use_explosion_element[2],       FALSE
366   },
367   {
368     EL_PLAYER_4,
369     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_4,
370     &li.use_explosion_element[3],       FALSE
371   },
372   {
373     EL_PLAYER_1,
374     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_5,
375     &li.continuous_snapping,            TRUE
376   },
377   {
378     EL_PLAYER_1,
379     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
380     &li.initial_player_stepsize,        STEPSIZE_NORMAL
381   },
382   {
383     EL_EMC_MAGIC_BALL,
384     TYPE_INTEGER,                       CONF_VALUE_INTEGER_1,
385     &li.ball_time,                      10
386   },
387   {
388     EL_EMC_MAGIC_BALL,
389     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_1,
390     &li.ball_random,                    FALSE
391   },
392   {
393     EL_EMC_MAGIC_BALL,
394     TYPE_BOOLEAN,                       CONF_VALUE_BOOLEAN_2,
395     &li.ball_state_initial,             FALSE
396   },
397
398   /* ---------- 2-byte values ---------------------------------------------- */
399
400   {
401     EL_PLAYER_1,
402     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_1,
403     &li.start_element[0],               EL_PLAYER_1
404   },
405   {
406     EL_PLAYER_2,
407     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_1,
408     &li.start_element[1],               EL_PLAYER_2
409   },
410   {
411     EL_PLAYER_3,
412     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_1,
413     &li.start_element[2],               EL_PLAYER_3
414   },
415   {
416     EL_PLAYER_4,
417     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_1,
418     &li.start_element[3],               EL_PLAYER_4
419   },
420   {
421     EL_PLAYER_1,
422     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_2,
423     &li.artwork_element[0],             EL_PLAYER_1
424   },
425   {
426     EL_PLAYER_2,
427     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_2,
428     &li.artwork_element[1],             EL_PLAYER_2
429   },
430   {
431     EL_PLAYER_3,
432     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_2,
433     &li.artwork_element[2],             EL_PLAYER_3
434   },
435   {
436     EL_PLAYER_4,
437     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_2,
438     &li.artwork_element[3],             EL_PLAYER_4
439   },
440   {
441     EL_PLAYER_1,
442     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_3,
443     &li.explosion_element[0],           EL_PLAYER_1
444   },
445   {
446     EL_PLAYER_2,
447     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_3,
448     &li.explosion_element[1],           EL_PLAYER_2
449   },
450   {
451     EL_PLAYER_3,
452     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_3,
453     &li.explosion_element[2],           EL_PLAYER_3
454   },
455   {
456     EL_PLAYER_4,
457     TYPE_ELEMENT,                       CONF_VALUE_ELEMENT_3,
458     &li.explosion_element[3],           EL_PLAYER_4
459   },
460
461   /* ---------- multi-byte values ------------------------------------------ */
462
463   {
464     EL_EMC_ANDROID,
465     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
466     &li.android_clone_element[0],       EL_EMPTY,
467     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
468   },
469   {
470     EL_EMC_MAGIC_BALL,
471     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
472     &li.ball_content,                   EL_EMPTY,
473     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
474   },
475
476   {
477     -1,
478     -1,                                 -1,
479     NULL,                               -1,
480   },
481 };
482
483 static struct ElementFileConfig custom_element_conf[] =
484 {
485   {
486     -1,
487     TYPE_STRING,                        CONF_VALUE_BYTES(1),
488     &xx_ei.description[0],              -1,
489     &xx_default_description_length,     -1, MAX_ELEMENT_NAME_LEN,
490     &xx_default_description[0]
491   },
492
493   {
494     -1,
495     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
496     &xx_ei.properties[EP_BITFIELD_BASE],
497 #if 0
498     EP_BITMASK_DEFAULT
499 #else
500     (1 << EP_CAN_MOVE_INTO_ACID)
501 #endif
502   },
503 #if 0
504   /* (reserved) */
505   {
506     -1,
507     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
508     &xx_ei.properties[EP_BITFIELD_BASE + 1], EP_BITMASK_DEFAULT
509   },
510 #endif
511
512   {
513     -1,
514     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
515     &xx_ei.use_gfx_element,             FALSE
516   },
517   {
518     -1,
519     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
520     &xx_ei.gfx_element,                 EL_EMPTY_SPACE
521   },
522
523   {
524     -1,
525     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
526     &xx_ei.access_direction,            MV_ALL_DIRECTIONS
527   },
528
529   {
530     -1,
531     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
532     &xx_ei.collect_score_initial,       10
533   },
534   {
535     -1,
536     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
537     &xx_ei.collect_count_initial,       1
538   },
539
540   {
541     -1,
542     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
543     &xx_ei.ce_value_fixed_initial,      0
544   },
545   {
546     -1,
547     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
548     &xx_ei.ce_value_random_initial,     0
549   },
550   {
551     -1,
552     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
553     &xx_ei.use_last_ce_value,           FALSE
554   },
555
556   {
557     -1,
558     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
559     &xx_ei.push_delay_fixed,            8,
560   },
561   {
562     -1,
563     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
564     &xx_ei.push_delay_random,           8,
565   },
566   {
567     -1,
568     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
569     &xx_ei.drop_delay_fixed,            0
570   },
571   {
572     -1,
573     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
574     &xx_ei.drop_delay_random,           0
575   },
576   {
577     -1,
578     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
579     &xx_ei.move_delay_fixed,            0
580   },
581   {
582     -1,
583     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
584     &xx_ei.move_delay_random,           0
585   },
586
587   {
588     -1,
589     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
590     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS
591   },
592   {
593     -1,
594     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
595     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC
596   },
597   {
598     -1,
599     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
600     &xx_ei.move_stepsize,               TILEX / 8
601   },
602
603   {
604     -1,
605     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
606     &xx_ei.move_enter_element,          EL_EMPTY_SPACE
607   },
608   {
609     -1,
610     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
611     &xx_ei.move_leave_element,          EL_EMPTY_SPACE
612   },
613   {
614     -1,
615     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
616     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED
617   },
618
619   {
620     -1,
621     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
622     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM
623   },
624
625   {
626     -1,
627     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
628     &xx_ei.explosion_type,              EXPLODES_3X3
629   },
630   {
631     -1,
632     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
633     &xx_ei.explosion_delay,             16
634   },
635   {
636     -1,
637     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
638     &xx_ei.ignition_delay,              8
639   },
640
641   {
642     -1,
643     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
644     &xx_ei.content,                     EL_EMPTY_SPACE,
645     &xx_num_contents,                   1, 1
646   },
647
648   /* ---------- "num_change_pages" must be the last entry ------------------- */
649
650   {
651     -1,
652     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
653     &xx_ei.num_change_pages,            -1      /* always save this value */
654   },
655
656   {
657     -1,
658     -1,                                 -1,
659     NULL,                               -1,
660   },
661 };
662
663 static struct ElementFileConfig custom_element_change_conf[] =
664 {
665   /* ---------- "current_change_page" must be the first entry --------------- */
666
667   {
668     -1,
669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
670     &xx_current_change_page,            -1
671   },
672
673   {
674     -1,
675     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
676     &xx_change.can_change,              FALSE
677   },
678
679   {
680     -1,
681     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
682     &xx_event_bits_0_31,                0
683   },
684   {
685     -1,
686     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
687     &xx_event_bits_32_63,               0
688   },
689
690   {
691     -1,
692     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
693     &xx_change.trigger_player,          CH_PLAYER_ANY
694   },
695   {
696     -1,
697     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
698     &xx_change.trigger_side,            CH_SIDE_ANY
699   },
700   {
701     -1,
702     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
703     &xx_change.trigger_page,            CH_PAGE_ANY
704   },
705
706   {
707     -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &xx_change.target_element,          EL_EMPTY_SPACE
710   },
711
712   {
713     -1,
714     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
715     &xx_change.delay_fixed,             0
716   },
717   {
718     -1,
719     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
720     &xx_change.delay_random,            0
721   },
722   {
723     -1,
724     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
725     &xx_change.delay_frames,            FRAMES_PER_SECOND
726   },
727
728   {
729     -1,
730     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
731     &xx_change.trigger_element,         EL_EMPTY_SPACE
732   },
733
734   {
735     -1,
736     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
737     &xx_change.explode,                 FALSE
738   },
739   {
740     -1,
741     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
742     &xx_change.use_target_content,      FALSE
743   },
744   {
745     -1,
746     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
747     &xx_change.only_if_complete,        FALSE
748   },
749   {
750     -1,
751     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
752     &xx_change.use_random_replace,      FALSE
753   },
754   {
755     -1,
756     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
757     &xx_change.random_percentage,       100
758   },
759   {
760     -1,
761     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
762     &xx_change.replace_when,            CP_WHEN_EMPTY
763   },
764
765   {
766     -1,
767     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
768     &xx_change.has_action,              FALSE
769   },
770   {
771     -1,
772     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
773     &xx_change.action_type,             CA_NO_ACTION
774   },
775   {
776     -1,
777     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
778     &xx_change.action_mode,             CA_MODE_UNDEFINED
779   },
780   {
781     -1,
782     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
783     &xx_change.action_arg,              CA_ARG_UNDEFINED
784   },
785
786   {
787     -1,
788     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
789     &xx_change.target_content,          EL_EMPTY_SPACE,
790     &xx_num_contents,                   1, 1
791   },
792
793   {
794     -1,
795     -1,                                 -1,
796     NULL,                               -1,
797   },
798 };
799
800 static struct ElementFileConfig group_element_conf[] =
801 {
802   {
803     -1,
804     TYPE_STRING,                        CONF_VALUE_BYTES(1),
805     &xx_ei.description[0],              -1,
806     &xx_default_description_length,     -1, MAX_ELEMENT_NAME_LEN,
807     &xx_default_description[0]
808   },
809
810   {
811     -1,
812     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
813     &xx_ei.use_gfx_element,             FALSE
814   },
815   {
816     -1,
817     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
818     &xx_ei.gfx_element,                 EL_EMPTY_SPACE
819   },
820
821   {
822     -1,
823     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
824     &xx_group.choice_mode,              ANIM_RANDOM
825   },
826
827   {
828     -1,
829     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
830     &xx_group.element[0],               EL_EMPTY_SPACE,
831     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
832   },
833
834   {
835     -1,
836     -1,                                 -1,
837     NULL,                               -1,
838   },
839 };
840
841 static struct
842 {
843   int filetype;
844   char *id;
845 }
846 filetype_id_list[] =
847 {
848   { LEVEL_FILE_TYPE_RND,        "RND"   },
849   { LEVEL_FILE_TYPE_BD,         "BD"    },
850   { LEVEL_FILE_TYPE_EM,         "EM"    },
851   { LEVEL_FILE_TYPE_SP,         "SP"    },
852   { LEVEL_FILE_TYPE_DX,         "DX"    },
853   { LEVEL_FILE_TYPE_SB,         "SB"    },
854   { LEVEL_FILE_TYPE_DC,         "DC"    },
855   { -1,                         NULL    },
856 };
857
858
859 /* ========================================================================= */
860 /* level file functions                                                      */
861 /* ========================================================================= */
862
863 static char *getDefaultElementDescription(struct ElementInfo *ei)
864 {
865   static char description[MAX_ELEMENT_NAME_LEN + 1];
866   char *default_description = (ei->custom_description != NULL ?
867                                ei->custom_description :
868                                ei->editor_description);
869   int i;
870
871   /* always start with reliable default values */
872   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
873     description[i] = '\0';
874
875   /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
876   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
877
878   return &description[0];
879 }
880
881 static void setElementDescriptionToDefault(struct ElementInfo *ei)
882 {
883   char *default_description = getDefaultElementDescription(ei);
884   int i;
885
886   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
887     ei->description[i] = default_description[i];
888 }
889
890 static void setLevelInfoToDefaultsFromConfigList(struct LevelInfo *level)
891 {
892   int i;
893
894   li = *level;          /* copy level data into temporary buffer */
895
896   for (i = 0; element_conf[i].data_type != -1; i++)
897   {
898     int default_value = element_conf[i].default_value;
899     int data_type = element_conf[i].data_type;
900     int conf_type = element_conf[i].conf_type;
901     int byte_mask = conf_type & CONF_MASK_BYTES;
902
903     if (byte_mask == CONF_MASK_MULTI_BYTES)
904     {
905       int default_num_entities = element_conf[i].default_num_entities;
906       int max_num_entities = element_conf[i].max_num_entities;
907
908       *(int *)(element_conf[i].num_entities) = default_num_entities;
909
910       if (data_type == TYPE_STRING)
911       {
912         char *default_string = element_conf[i].default_string;
913         char *string = (char *)(element_conf[i].value);
914
915         strncpy(string, default_string, max_num_entities);
916       }
917       else if (data_type == TYPE_ELEMENT_LIST)
918       {
919         int *element_array = (int *)(element_conf[i].value);
920         int j;
921
922         for (j = 0; j < max_num_entities; j++)
923           element_array[j] = default_value;
924       }
925       else if (data_type == TYPE_CONTENT_LIST)
926       {
927         struct Content *content = (struct Content *)(element_conf[i].value);
928         int c, x, y;
929
930         for (c = 0; c < max_num_entities; c++)
931           for (y = 0; y < 3; y++)
932             for (x = 0; x < 3; x++)
933               content[c].e[x][y] = default_value;
934       }
935     }
936     else        /* constant size configuration data (1, 2 or 4 bytes) */
937     {
938       if (data_type == TYPE_BOOLEAN)
939         *(boolean *)(element_conf[i].value) = default_value;
940       else
941         *(int *)    (element_conf[i].value) = default_value;
942     }
943   }
944
945   *level = li;          /* copy temporary buffer back to level data */
946 }
947
948 void setElementChangePages(struct ElementInfo *ei, int change_pages)
949 {
950   int change_page_size = sizeof(struct ElementChangeInfo);
951
952   ei->num_change_pages = MAX(1, change_pages);
953
954   ei->change_page =
955     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
956
957   if (ei->current_change_page >= ei->num_change_pages)
958     ei->current_change_page = ei->num_change_pages - 1;
959
960   ei->change = &ei->change_page[ei->current_change_page];
961 }
962
963 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
964 {
965   int i, x, y;
966
967   change->can_change = FALSE;
968
969   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
970     change->has_event[i] = FALSE;
971
972   change->trigger_player = CH_PLAYER_ANY;
973   change->trigger_side = CH_SIDE_ANY;
974   change->trigger_page = CH_PAGE_ANY;
975
976   change->target_element = EL_EMPTY_SPACE;
977
978   change->delay_fixed = 0;
979   change->delay_random = 0;
980   change->delay_frames = FRAMES_PER_SECOND;
981
982   change->trigger_element = EL_EMPTY_SPACE;
983
984   change->explode = FALSE;
985   change->use_target_content = FALSE;
986   change->only_if_complete = FALSE;
987   change->use_random_replace = FALSE;
988   change->random_percentage = 100;
989   change->replace_when = CP_WHEN_EMPTY;
990
991   change->has_action = FALSE;
992   change->action_type = CA_NO_ACTION;
993   change->action_mode = CA_MODE_UNDEFINED;
994   change->action_arg = CA_ARG_UNDEFINED;
995
996   for (x = 0; x < 3; x++)
997     for (y = 0; y < 3; y++)
998       change->target_content.e[x][y] = EL_EMPTY_SPACE;
999
1000   change->direct_action = 0;
1001   change->other_action = 0;
1002
1003   change->pre_change_function = NULL;
1004   change->change_function = NULL;
1005   change->post_change_function = NULL;
1006 }
1007
1008 static void setLevelInfoToDefaults(struct LevelInfo *level)
1009 {
1010   static boolean clipboard_elements_initialized = FALSE;
1011   int i, j, x, y;
1012
1013 #if 1
1014   InitElementPropertiesStatic();
1015 #endif
1016
1017   setLevelInfoToDefaultsFromConfigList(level);
1018   setLevelInfoToDefaults_EM();
1019
1020   level->native_em_level = &native_em_level;
1021
1022   level->game_engine_type = GAME_ENGINE_TYPE_RND;
1023
1024   level->file_version = FILE_VERSION_ACTUAL;
1025   level->game_version = GAME_VERSION_ACTUAL;
1026
1027   level->encoding_16bit_field  = FALSE; /* default: only 8-bit elements */
1028   level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
1029   level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
1030
1031   level->fieldx = STD_LEV_FIELDX;
1032   level->fieldy = STD_LEV_FIELDY;
1033
1034   for (x = 0; x < MAX_LEV_FIELDX; x++)
1035     for (y = 0; y < MAX_LEV_FIELDY; y++)
1036       level->field[x][y] = EL_SAND;
1037
1038   level->time = 100;
1039   level->gems_needed = 0;
1040
1041   level->amoeba_speed = 10;
1042
1043   level->time_magic_wall = 10;
1044   level->time_wheel = 10;
1045 #if 0
1046   level->time_light = 10;
1047   level->time_timegate = 10;
1048 #endif
1049
1050   level->amoeba_content = EL_DIAMOND;
1051
1052   level->game_of_life[0] = 2;
1053   level->game_of_life[1] = 3;
1054   level->game_of_life[2] = 3;
1055   level->game_of_life[3] = 3;
1056
1057   level->biomaze[0] = 2;
1058   level->biomaze[1] = 3;
1059   level->biomaze[2] = 3;
1060   level->biomaze[3] = 3;
1061
1062 #if 0
1063   level->double_speed = FALSE;
1064 #endif
1065   level->initial_gravity = FALSE;
1066   level->em_slippery_gems = FALSE;
1067   level->instant_relocation = FALSE;
1068   level->can_pass_to_walkable = FALSE;
1069   level->grow_into_diggable = TRUE;
1070
1071 #if 0
1072   level->block_snap_field = TRUE;
1073 #endif
1074
1075   level->block_last_field = FALSE;      /* EM does not block by default */
1076   level->sp_block_last_field = TRUE;    /* SP blocks the last field */
1077
1078   level->can_move_into_acid_bits = ~0;  /* everything can move into acid */
1079   level->dont_collide_with_bits = ~0;   /* always deadly when colliding */
1080
1081   level->use_spring_bug = FALSE;
1082   level->use_time_orb_bug = FALSE;
1083
1084   level->use_step_counter = FALSE;
1085
1086   /* values for the new EMC elements */
1087 #if 0
1088   level->android_move_time = 10;
1089   level->android_clone_time = 10;
1090   level->ball_time = 10;
1091   level->lenses_score = 10;
1092   level->lenses_time = 10;
1093   level->magnify_score = 10;
1094   level->magnify_time = 10;
1095   level->slurp_score = 10;
1096   level->wind_direction_initial = MV_NONE;
1097   level->ball_random = FALSE;
1098   level->ball_state_initial = FALSE;
1099   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1100     for (x = 0; x < 3; x++)
1101       for (y = 0; y < 3; y++)
1102         level->ball_content[i].e[x][y] = EL_EMPTY;
1103 #endif
1104 #if 0
1105   for (i = 0; i < 16; i++)
1106     level->android_array[i] = FALSE;
1107 #endif
1108
1109   level->use_custom_template = FALSE;
1110
1111   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1112     level->name[i] = '\0';
1113   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1114     level->author[i] = '\0';
1115
1116   strcpy(level->name, NAMELESS_LEVEL_NAME);
1117   strcpy(level->author, ANONYMOUS_NAME);
1118
1119   for (i = 0; i < 4; i++)
1120   {
1121     level->envelope_text[i][0] = '\0';
1122     level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
1123     level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
1124   }
1125
1126   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1127     level->score[i] = (i == SC_TIME_BONUS ? 1 : 10);
1128
1129   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1130   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1131     for (x = 0; x < 3; x++)
1132       for (y = 0; y < 3; y++)
1133         level->yamyam_content[i].e[x][y] =
1134           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
1135
1136   level->field[0][0] = EL_PLAYER_1;
1137   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1138
1139   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1140   {
1141     int element = i;
1142     struct ElementInfo *ei = &element_info[element];
1143
1144     /* never initialize clipboard elements after the very first time */
1145     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1146       continue;
1147
1148     setElementChangePages(ei, 1);
1149     setElementChangeInfoToDefaults(ei->change);
1150
1151     if (IS_CUSTOM_ELEMENT(element) ||
1152         IS_GROUP_ELEMENT(element) ||
1153         IS_INTERNAL_ELEMENT(element))
1154     {
1155 #if 1
1156       setElementDescriptionToDefault(ei);
1157 #else
1158       for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
1159         ei->description[j] = '\0';
1160
1161       if (ei->custom_description != NULL)
1162         strncpy(ei->description, ei->custom_description,MAX_ELEMENT_NAME_LEN);
1163       else
1164         strcpy(ei->description, ei->editor_description);
1165 #endif
1166
1167       ei->use_gfx_element = FALSE;
1168       ei->gfx_element = EL_EMPTY_SPACE;
1169
1170       ei->modified_settings = FALSE;
1171     }
1172
1173     if (IS_CUSTOM_ELEMENT(element) ||
1174         IS_INTERNAL_ELEMENT(element))
1175     {
1176       ei->access_direction = MV_ALL_DIRECTIONS;
1177
1178       ei->collect_score_initial = 10;   /* special default */
1179       ei->collect_count_initial = 1;    /* special default */
1180
1181       ei->ce_value_fixed_initial = 0;
1182       ei->ce_value_random_initial = 0;
1183       ei->use_last_ce_value = FALSE;
1184
1185       ei->push_delay_fixed = -1;        /* initialize later */
1186       ei->push_delay_random = -1;       /* initialize later */
1187       ei->drop_delay_fixed = 0;
1188       ei->drop_delay_random = 0;
1189       ei->move_delay_fixed = 0;
1190       ei->move_delay_random = 0;
1191
1192       ei->move_pattern = MV_ALL_DIRECTIONS;
1193       ei->move_direction_initial = MV_START_AUTOMATIC;
1194       ei->move_stepsize = TILEX / 8;
1195
1196       ei->move_enter_element = EL_EMPTY_SPACE;
1197       ei->move_leave_element = EL_EMPTY_SPACE;
1198       ei->move_leave_type = LEAVE_TYPE_UNLIMITED;
1199
1200       ei->slippery_type = SLIPPERY_ANY_RANDOM;
1201
1202       ei->explosion_type = EXPLODES_3X3;
1203       ei->explosion_delay = 16;
1204       ei->ignition_delay = 8;
1205
1206       for (x = 0; x < 3; x++)
1207         for (y = 0; y < 3; y++)
1208           ei->content.e[x][y] = EL_EMPTY_SPACE;
1209
1210       ei->access_type = 0;
1211       ei->access_layer = 0;
1212       ei->access_protected = 0;
1213       ei->walk_to_action = 0;
1214       ei->smash_targets = 0;
1215       ei->deadliness = 0;
1216
1217       ei->can_explode_by_fire = FALSE;
1218       ei->can_explode_smashed = FALSE;
1219       ei->can_explode_impact = FALSE;
1220
1221       ei->current_change_page = 0;
1222
1223 #if 0
1224       /* !!! now done in InitElementPropertiesStatic() (see above) !!! */
1225       /* !!! (else properties set there will be overwritten here)  !!! */
1226       /* start with no properties at all */
1227 #if 1
1228       for (j = 0; j < NUM_EP_BITFIELDS; j++)
1229         ei->properties[j] = EP_BITMASK_DEFAULT;
1230 #else
1231       for (j = 0; j < NUM_EP_BITFIELDS; j++)
1232         Properties[element][j] = EP_BITMASK_DEFAULT;
1233 #endif
1234 #endif
1235
1236       /* now set default properties */
1237       SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
1238     }
1239
1240     if (IS_GROUP_ELEMENT(element) ||
1241         IS_INTERNAL_ELEMENT(element))
1242     {
1243       struct ElementGroupInfo *group;
1244
1245       /* initialize memory for list of elements in group */
1246       if (ei->group == NULL)
1247         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1248
1249       group = ei->group;
1250
1251       for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
1252         group->element[j] = EL_EMPTY_SPACE;
1253
1254       /* default: only one element in group */
1255       group->num_elements = 1;
1256
1257       group->choice_mode = ANIM_RANDOM;
1258     }
1259   }
1260
1261   clipboard_elements_initialized = TRUE;
1262
1263   BorderElement = EL_STEELWALL;
1264
1265   level->no_valid_file = FALSE;
1266
1267   level->changed = FALSE;
1268
1269   if (leveldir_current == NULL)         /* only when dumping level */
1270     return;
1271
1272   /* try to determine better author name than 'anonymous' */
1273   if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1274   {
1275     strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1276     level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1277   }
1278   else
1279   {
1280     switch (LEVELCLASS(leveldir_current))
1281     {
1282       case LEVELCLASS_TUTORIAL:
1283         strcpy(level->author, PROGRAM_AUTHOR_STRING);
1284         break;
1285
1286       case LEVELCLASS_CONTRIB:
1287         strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1288         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1289         break;
1290
1291       case LEVELCLASS_PRIVATE:
1292         strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1293         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1294         break;
1295
1296       default:
1297         /* keep default value */
1298         break;
1299     }
1300   }
1301 }
1302
1303 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1304 {
1305   level_file_info->nr = 0;
1306   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1307   level_file_info->packed = FALSE;
1308   level_file_info->basename = NULL;
1309   level_file_info->filename = NULL;
1310 }
1311
1312 static void ActivateLevelTemplate()
1313 {
1314 #if 1
1315   /* Currently there is no special action needed to activate the template
1316      data, because 'element_info' property settings overwrite the original
1317      level data, while all other variables do not change. */
1318 #else
1319   /* Currently there is no special action needed to activate the template
1320      data, because 'element_info' and 'Properties' overwrite the original
1321      level data, while all other variables do not change. */
1322 #endif
1323 }
1324
1325 static char *getLevelFilenameFromBasename(char *basename)
1326 {
1327   static char *filename = NULL;
1328
1329   checked_free(filename);
1330
1331   filename = getPath2(getCurrentLevelDir(), basename);
1332
1333   return filename;
1334 }
1335
1336 static int getFileTypeFromBasename(char *basename)
1337 {
1338   static char *filename = NULL;
1339   struct stat file_status;
1340
1341   /* ---------- try to determine file type from filename ---------- */
1342
1343   /* check for typical filename of a Supaplex level package file */
1344   if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
1345                                  strncmp(basename, "LEVELS.D", 8) == 0))
1346     return LEVEL_FILE_TYPE_SP;
1347
1348   /* ---------- try to determine file type from filesize ---------- */
1349
1350   checked_free(filename);
1351   filename = getPath2(getCurrentLevelDir(), basename);
1352
1353   if (stat(filename, &file_status) == 0)
1354   {
1355     /* check for typical filesize of a Supaplex level package file */
1356     if (file_status.st_size == 170496)
1357       return LEVEL_FILE_TYPE_SP;
1358   }
1359
1360   return LEVEL_FILE_TYPE_UNKNOWN;
1361 }
1362
1363 static char *getSingleLevelBasename(int nr)
1364 {
1365   static char basename[MAX_FILENAME_LEN];
1366
1367   if (nr < 0)
1368     sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
1369   else
1370     sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
1371
1372   return basename;
1373 }
1374
1375 static char *getPackedLevelBasename(int type)
1376 {
1377   static char basename[MAX_FILENAME_LEN];
1378   char *directory = getCurrentLevelDir();
1379   DIR *dir;
1380   struct dirent *dir_entry;
1381
1382   strcpy(basename, UNDEFINED_FILENAME);         /* default: undefined file */
1383
1384   if ((dir = opendir(directory)) == NULL)
1385   {
1386     Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1387
1388     return basename;
1389   }
1390
1391   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
1392   {
1393     char *entry_basename = dir_entry->d_name;
1394     int entry_type = getFileTypeFromBasename(entry_basename);
1395
1396     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  /* found valid level package */
1397     {
1398       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1399           type == entry_type)
1400       {
1401         strcpy(basename, entry_basename);
1402
1403         break;
1404       }
1405     }
1406   }
1407
1408   closedir(dir);
1409
1410   return basename;
1411 }
1412
1413 static char *getSingleLevelFilename(int nr)
1414 {
1415   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1416 }
1417
1418 #if 0
1419 static char *getPackedLevelFilename(int type)
1420 {
1421   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1422 }
1423 #endif
1424
1425 char *getDefaultLevelFilename(int nr)
1426 {
1427   return getSingleLevelFilename(nr);
1428 }
1429
1430 #if 0
1431 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
1432                                                  int type)
1433 {
1434   lfi->type = type;
1435   lfi->packed = FALSE;
1436   lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
1437   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1438 }
1439 #endif
1440
1441 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
1442                                                  int type, char *format, ...)
1443 {
1444   static char basename[MAX_FILENAME_LEN];
1445   va_list ap;
1446
1447   va_start(ap, format);
1448   vsprintf(basename, format, ap);
1449   va_end(ap);
1450
1451   lfi->type = type;
1452   lfi->packed = FALSE;
1453   lfi->basename = basename;
1454   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1455 }
1456
1457 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
1458                                                  int type)
1459 {
1460   lfi->type = type;
1461   lfi->packed = TRUE;
1462   lfi->basename = getPackedLevelBasename(lfi->type);
1463   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1464 }
1465
1466 static int getFiletypeFromID(char *filetype_id)
1467 {
1468   char *filetype_id_lower;
1469   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
1470   int i;
1471
1472   if (filetype_id == NULL)
1473     return LEVEL_FILE_TYPE_UNKNOWN;
1474
1475   filetype_id_lower = getStringToLower(filetype_id);
1476
1477   for (i = 0; filetype_id_list[i].id != NULL; i++)
1478   {
1479     char *id_lower = getStringToLower(filetype_id_list[i].id);
1480     
1481     if (strEqual(filetype_id_lower, id_lower))
1482       filetype = filetype_id_list[i].filetype;
1483
1484     free(id_lower);
1485
1486     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
1487       break;
1488   }
1489
1490   free(filetype_id_lower);
1491
1492   return filetype;
1493 }
1494
1495 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
1496 {
1497   int nr = lfi->nr;
1498
1499   /* special case: level number is negative => check for level template file */
1500   if (nr < 0)
1501   {
1502     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1503                                          "template.%s", LEVELFILE_EXTENSION);
1504
1505     /* no fallback if template file not existing */
1506     return;
1507   }
1508
1509   /* special case: check for file name/pattern specified in "levelinfo.conf" */
1510   if (leveldir_current->level_filename != NULL)
1511   {
1512     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
1513
1514     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
1515                                          leveldir_current->level_filename, nr);
1516     if (fileExists(lfi->filename))
1517       return;
1518   }
1519
1520   /* check for native Rocks'n'Diamonds level file */
1521   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1522                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
1523   if (fileExists(lfi->filename))
1524     return;
1525
1526   /* check for Emerald Mine level file (V1) */
1527   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
1528                                        'a' + (nr / 10) % 26, '0' + nr % 10);
1529   if (fileExists(lfi->filename))
1530     return;
1531   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
1532                                        'A' + (nr / 10) % 26, '0' + nr % 10);
1533   if (fileExists(lfi->filename))
1534     return;
1535
1536   /* check for Emerald Mine level file (V2 to V5) */
1537   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
1538   if (fileExists(lfi->filename))
1539     return;
1540
1541   /* check for Emerald Mine level file (V6 / single mode) */
1542   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
1543   if (fileExists(lfi->filename))
1544     return;
1545   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
1546   if (fileExists(lfi->filename))
1547     return;
1548
1549   /* check for Emerald Mine level file (V6 / teamwork mode) */
1550   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
1551   if (fileExists(lfi->filename))
1552     return;
1553   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
1554   if (fileExists(lfi->filename))
1555     return;
1556
1557   /* check for various packed level file formats */
1558   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
1559   if (fileExists(lfi->filename))
1560     return;
1561
1562   /* no known level file found -- use default values (and fail later) */
1563   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
1564                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
1565 }
1566
1567 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
1568 {
1569   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
1570     lfi->type = getFileTypeFromBasename(lfi->basename);
1571 }
1572
1573 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
1574 {
1575   /* always start with reliable default values */
1576   setFileInfoToDefaults(level_file_info);
1577
1578   level_file_info->nr = nr;     /* set requested level number */
1579
1580   determineLevelFileInfo_Filename(level_file_info);
1581   determineLevelFileInfo_Filetype(level_file_info);
1582 }
1583
1584 /* ------------------------------------------------------------------------- */
1585 /* functions for loading R'n'D level                                         */
1586 /* ------------------------------------------------------------------------- */
1587
1588 int getMappedElement(int element)
1589 {
1590   /* remap some (historic, now obsolete) elements */
1591
1592   switch (element)
1593   {
1594     case EL_PLAYER_OBSOLETE:
1595       element = EL_PLAYER_1;
1596       break;
1597
1598     case EL_KEY_OBSOLETE:
1599       element = EL_KEY_1;
1600
1601     case EL_EM_KEY_1_FILE_OBSOLETE:
1602       element = EL_EM_KEY_1;
1603       break;
1604
1605     case EL_EM_KEY_2_FILE_OBSOLETE:
1606       element = EL_EM_KEY_2;
1607       break;
1608
1609     case EL_EM_KEY_3_FILE_OBSOLETE:
1610       element = EL_EM_KEY_3;
1611       break;
1612
1613     case EL_EM_KEY_4_FILE_OBSOLETE:
1614       element = EL_EM_KEY_4;
1615       break;
1616
1617     case EL_ENVELOPE_OBSOLETE:
1618       element = EL_ENVELOPE_1;
1619       break;
1620
1621     case EL_SP_EMPTY:
1622       element = EL_EMPTY;
1623       break;
1624
1625     default:
1626       if (element >= NUM_FILE_ELEMENTS)
1627       {
1628         Error(ERR_WARN, "invalid level element %d", element);
1629
1630         element = EL_UNKNOWN;
1631       }
1632       break;
1633   }
1634
1635   return element;
1636 }
1637
1638 int getMappedElementByVersion(int element, int game_version)
1639 {
1640   /* remap some elements due to certain game version */
1641
1642   if (game_version <= VERSION_IDENT(2,2,0,0))
1643   {
1644     /* map game font elements */
1645     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
1646                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1647                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
1648                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
1649   }
1650
1651   if (game_version < VERSION_IDENT(3,0,0,0))
1652   {
1653     /* map Supaplex gravity tube elements */
1654     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
1655                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1656                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
1657                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
1658                element);
1659   }
1660
1661   return element;
1662 }
1663
1664 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
1665 {
1666   level->file_version = getFileVersion(file);
1667   level->game_version = getFileVersion(file);
1668
1669   return chunk_size;
1670 }
1671
1672 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
1673 {
1674   int i, x, y;
1675
1676   level->fieldx = getFile8Bit(file);
1677   level->fieldy = getFile8Bit(file);
1678
1679   level->time           = getFile16BitBE(file);
1680   level->gems_needed    = getFile16BitBE(file);
1681
1682   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1683     level->name[i] = getFile8Bit(file);
1684   level->name[MAX_LEVEL_NAME_LEN] = 0;
1685
1686   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1687     level->score[i] = getFile8Bit(file);
1688
1689   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1690   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1691     for (y = 0; y < 3; y++)
1692       for (x = 0; x < 3; x++)
1693         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
1694
1695   level->amoeba_speed           = getFile8Bit(file);
1696   level->time_magic_wall        = getFile8Bit(file);
1697   level->time_wheel             = getFile8Bit(file);
1698   level->amoeba_content         = getMappedElement(getFile8Bit(file));
1699
1700   level->initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
1701                                     STEPSIZE_NORMAL);
1702
1703   level->initial_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1704   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1705   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1706
1707   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1708
1709   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1710   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1711   level->can_move_into_acid_bits = getFile32BitBE(file);
1712   level->dont_collide_with_bits = getFile8Bit(file);
1713
1714   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1715   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1716
1717   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1718   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1719   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
1720
1721   level->game_engine_type       = getFile8Bit(file);
1722
1723   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
1724
1725   return chunk_size;
1726 }
1727
1728 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
1729 {
1730   int i;
1731
1732   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1733     level->author[i] = getFile8Bit(file);
1734   level->author[MAX_LEVEL_NAME_LEN] = 0;
1735
1736   return chunk_size;
1737 }
1738
1739 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
1740 {
1741   int x, y;
1742   int chunk_size_expected = level->fieldx * level->fieldy;
1743
1744   /* Note: "chunk_size" was wrong before version 2.0 when elements are
1745      stored with 16-bit encoding (and should be twice as big then).
1746      Even worse, playfield data was stored 16-bit when only yamyam content
1747      contained 16-bit elements and vice versa. */
1748
1749   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
1750     chunk_size_expected *= 2;
1751
1752   if (chunk_size_expected != chunk_size)
1753   {
1754     ReadUnusedBytesFromFile(file, chunk_size);
1755     return chunk_size_expected;
1756   }
1757
1758   for (y = 0; y < level->fieldy; y++)
1759     for (x = 0; x < level->fieldx; x++)
1760       level->field[x][y] =
1761         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
1762                          getFile8Bit(file));
1763   return chunk_size;
1764 }
1765
1766 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
1767 {
1768   int i, x, y;
1769   int header_size = 4;
1770   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
1771   int chunk_size_expected = header_size + content_size;
1772
1773   /* Note: "chunk_size" was wrong before version 2.0 when elements are
1774      stored with 16-bit encoding (and should be twice as big then).
1775      Even worse, playfield data was stored 16-bit when only yamyam content
1776      contained 16-bit elements and vice versa. */
1777
1778   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
1779     chunk_size_expected += content_size;
1780
1781   if (chunk_size_expected != chunk_size)
1782   {
1783     ReadUnusedBytesFromFile(file, chunk_size);
1784     return chunk_size_expected;
1785   }
1786
1787   getFile8Bit(file);
1788   level->num_yamyam_contents = getFile8Bit(file);
1789   getFile8Bit(file);
1790   getFile8Bit(file);
1791
1792   /* correct invalid number of content fields -- should never happen */
1793   if (level->num_yamyam_contents < 1 ||
1794       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
1795     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
1796
1797   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1798     for (y = 0; y < 3; y++)
1799       for (x = 0; x < 3; x++)
1800         level->yamyam_content[i].e[x][y] =
1801           getMappedElement(level->encoding_16bit_field ?
1802                            getFile16BitBE(file) : getFile8Bit(file));
1803   return chunk_size;
1804 }
1805
1806 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
1807 {
1808   int i, x, y;
1809   int element;
1810   int num_contents, content_xsize, content_ysize;
1811   int content_array[MAX_ELEMENT_CONTENTS][3][3];
1812
1813   element = getMappedElement(getFile16BitBE(file));
1814   num_contents = getFile8Bit(file);
1815   content_xsize = getFile8Bit(file);
1816   content_ysize = getFile8Bit(file);
1817
1818   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1819
1820   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1821     for (y = 0; y < 3; y++)
1822       for (x = 0; x < 3; x++)
1823         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
1824
1825   /* correct invalid number of content fields -- should never happen */
1826   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
1827     num_contents = STD_ELEMENT_CONTENTS;
1828
1829   if (element == EL_YAMYAM)
1830   {
1831     level->num_yamyam_contents = num_contents;
1832
1833     for (i = 0; i < num_contents; i++)
1834       for (y = 0; y < 3; y++)
1835         for (x = 0; x < 3; x++)
1836           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
1837   }
1838   else if (element == EL_BD_AMOEBA)
1839   {
1840     level->amoeba_content = content_array[0][0][0];
1841   }
1842   else
1843   {
1844     Error(ERR_WARN, "cannot load content for element '%d'", element);
1845   }
1846
1847   return chunk_size;
1848 }
1849
1850 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
1851 {
1852   int i;
1853   int element;
1854   int envelope_nr;
1855   int envelope_len;
1856   int chunk_size_expected;
1857
1858   element = getMappedElement(getFile16BitBE(file));
1859   if (!IS_ENVELOPE(element))
1860     element = EL_ENVELOPE_1;
1861
1862   envelope_nr = element - EL_ENVELOPE_1;
1863
1864   envelope_len = getFile16BitBE(file);
1865
1866   level->envelope_xsize[envelope_nr] = getFile8Bit(file);
1867   level->envelope_ysize[envelope_nr] = getFile8Bit(file);
1868
1869   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1870
1871   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
1872   if (chunk_size_expected != chunk_size)
1873   {
1874     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
1875     return chunk_size_expected;
1876   }
1877
1878   for (i = 0; i < envelope_len; i++)
1879     level->envelope_text[envelope_nr][i] = getFile8Bit(file);
1880
1881   return chunk_size;
1882 }
1883
1884 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
1885 {
1886   int num_changed_custom_elements = getFile16BitBE(file);
1887   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
1888   int i;
1889
1890   if (chunk_size_expected != chunk_size)
1891   {
1892     ReadUnusedBytesFromFile(file, chunk_size - 2);
1893     return chunk_size_expected;
1894   }
1895
1896   for (i = 0; i < num_changed_custom_elements; i++)
1897   {
1898     int element = getFile16BitBE(file);
1899     int properties = getFile32BitBE(file);
1900
1901 #if 1
1902     if (IS_CUSTOM_ELEMENT(element))
1903       element_info[element].properties[EP_BITFIELD_BASE] = properties;
1904     else
1905       Error(ERR_WARN, "invalid custom element number %d", element);
1906 #else
1907     if (IS_CUSTOM_ELEMENT(element))
1908       Properties[element][EP_BITFIELD_BASE] = properties;
1909     else
1910       Error(ERR_WARN, "invalid custom element number %d", element);
1911 #endif
1912   }
1913
1914   return chunk_size;
1915 }
1916
1917 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
1918 {
1919   int num_changed_custom_elements = getFile16BitBE(file);
1920   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
1921   int i;
1922
1923   if (chunk_size_expected != chunk_size)
1924   {
1925     ReadUnusedBytesFromFile(file, chunk_size - 2);
1926     return chunk_size_expected;
1927   }
1928
1929   for (i = 0; i < num_changed_custom_elements; i++)
1930   {
1931     int element = getFile16BitBE(file);
1932     int custom_target_element = getFile16BitBE(file);
1933
1934     if (IS_CUSTOM_ELEMENT(element))
1935       element_info[element].change->target_element = custom_target_element;
1936     else
1937       Error(ERR_WARN, "invalid custom element number %d", element);
1938   }
1939
1940   return chunk_size;
1941 }
1942
1943 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
1944 {
1945   int num_changed_custom_elements = getFile16BitBE(file);
1946   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1947   int i, j, x, y;
1948
1949   if (chunk_size_expected != chunk_size)
1950   {
1951     ReadUnusedBytesFromFile(file, chunk_size - 2);
1952     return chunk_size_expected;
1953   }
1954
1955   for (i = 0; i < num_changed_custom_elements; i++)
1956   {
1957     int element = getFile16BitBE(file);
1958     struct ElementInfo *ei = &element_info[element];
1959     unsigned int event_bits;
1960
1961     if (!IS_CUSTOM_ELEMENT(element))
1962     {
1963       Error(ERR_WARN, "invalid custom element number %d", element);
1964
1965       element = EL_INTERNAL_DUMMY;
1966     }
1967
1968     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1969       ei->description[j] = getFile8Bit(file);
1970     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1971
1972 #if 1
1973     ei->properties[EP_BITFIELD_BASE] = getFile32BitBE(file);
1974 #else
1975     Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1976 #endif
1977
1978     /* some free bytes for future properties and padding */
1979     ReadUnusedBytesFromFile(file, 7);
1980
1981     ei->use_gfx_element = getFile8Bit(file);
1982     ei->gfx_element = getMappedElement(getFile16BitBE(file));
1983
1984     ei->collect_score_initial = getFile8Bit(file);
1985     ei->collect_count_initial = getFile8Bit(file);
1986
1987     ei->push_delay_fixed = getFile16BitBE(file);
1988     ei->push_delay_random = getFile16BitBE(file);
1989     ei->move_delay_fixed = getFile16BitBE(file);
1990     ei->move_delay_random = getFile16BitBE(file);
1991
1992     ei->move_pattern = getFile16BitBE(file);
1993     ei->move_direction_initial = getFile8Bit(file);
1994     ei->move_stepsize = getFile8Bit(file);
1995
1996     for (y = 0; y < 3; y++)
1997       for (x = 0; x < 3; x++)
1998         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
1999
2000     event_bits = getFile32BitBE(file);
2001     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2002       if (event_bits & (1 << j))
2003         ei->change->has_event[j] = TRUE;
2004
2005     ei->change->target_element = getMappedElement(getFile16BitBE(file));
2006
2007     ei->change->delay_fixed = getFile16BitBE(file);
2008     ei->change->delay_random = getFile16BitBE(file);
2009     ei->change->delay_frames = getFile16BitBE(file);
2010
2011     ei->change->trigger_element = getMappedElement(getFile16BitBE(file));
2012
2013     ei->change->explode = getFile8Bit(file);
2014     ei->change->use_target_content = getFile8Bit(file);
2015     ei->change->only_if_complete = getFile8Bit(file);
2016     ei->change->use_random_replace = getFile8Bit(file);
2017
2018     ei->change->random_percentage = getFile8Bit(file);
2019     ei->change->replace_when = getFile8Bit(file);
2020
2021     for (y = 0; y < 3; y++)
2022       for (x = 0; x < 3; x++)
2023         ei->change->target_content.e[x][y] =
2024           getMappedElement(getFile16BitBE(file));
2025
2026     ei->slippery_type = getFile8Bit(file);
2027
2028     /* some free bytes for future properties and padding */
2029     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2030
2031     /* mark that this custom element has been modified */
2032     ei->modified_settings = TRUE;
2033   }
2034
2035   return chunk_size;
2036 }
2037
2038 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
2039 {
2040   struct ElementInfo *ei;
2041   int chunk_size_expected;
2042   int element;
2043   int i, j, x, y;
2044
2045   /* ---------- custom element base property values (96 bytes) ------------- */
2046
2047   element = getFile16BitBE(file);
2048
2049   if (!IS_CUSTOM_ELEMENT(element))
2050   {
2051     Error(ERR_WARN, "invalid custom element number %d", element);
2052
2053     ReadUnusedBytesFromFile(file, chunk_size - 2);
2054     return chunk_size;
2055   }
2056
2057   ei = &element_info[element];
2058
2059   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2060     ei->description[i] = getFile8Bit(file);
2061   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2062
2063 #if 1
2064   ei->properties[EP_BITFIELD_BASE] = getFile32BitBE(file);
2065 #else
2066   Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
2067 #endif
2068   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
2069
2070   ei->num_change_pages = getFile8Bit(file);
2071
2072   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2073   if (chunk_size_expected != chunk_size)
2074   {
2075     ReadUnusedBytesFromFile(file, chunk_size - 43);
2076     return chunk_size_expected;
2077   }
2078
2079   ei->ce_value_fixed_initial = getFile16BitBE(file);
2080   ei->ce_value_random_initial = getFile16BitBE(file);
2081   ei->use_last_ce_value = getFile8Bit(file);
2082
2083   ei->use_gfx_element = getFile8Bit(file);
2084   ei->gfx_element = getMappedElement(getFile16BitBE(file));
2085
2086   ei->collect_score_initial = getFile8Bit(file);
2087   ei->collect_count_initial = getFile8Bit(file);
2088
2089   ei->drop_delay_fixed = getFile8Bit(file);
2090   ei->push_delay_fixed = getFile8Bit(file);
2091   ei->drop_delay_random = getFile8Bit(file);
2092   ei->push_delay_random = getFile8Bit(file);
2093   ei->move_delay_fixed = getFile16BitBE(file);
2094   ei->move_delay_random = getFile16BitBE(file);
2095
2096   /* bits 0 - 15 of "move_pattern" ... */
2097   ei->move_pattern = getFile16BitBE(file);
2098   ei->move_direction_initial = getFile8Bit(file);
2099   ei->move_stepsize = getFile8Bit(file);
2100
2101   ei->slippery_type = getFile8Bit(file);
2102
2103   for (y = 0; y < 3; y++)
2104     for (x = 0; x < 3; x++)
2105       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2106
2107   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2108   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2109   ei->move_leave_type = getFile8Bit(file);
2110
2111   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2112   ei->move_pattern |= (getFile16BitBE(file) << 16);
2113
2114   ei->access_direction = getFile8Bit(file);
2115
2116   ei->explosion_delay = getFile8Bit(file);
2117   ei->ignition_delay = getFile8Bit(file);
2118   ei->explosion_type = getFile8Bit(file);
2119
2120   /* some free bytes for future custom property values and padding */
2121   ReadUnusedBytesFromFile(file, 1);
2122
2123   /* ---------- change page property values (48 bytes) --------------------- */
2124
2125   setElementChangePages(ei, ei->num_change_pages);
2126
2127   for (i = 0; i < ei->num_change_pages; i++)
2128   {
2129     struct ElementChangeInfo *change = &ei->change_page[i];
2130     unsigned int event_bits;
2131
2132     /* always start with reliable default values */
2133     setElementChangeInfoToDefaults(change);
2134
2135     /* bits 0 - 31 of "has_event[]" ... */
2136     event_bits = getFile32BitBE(file);
2137     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2138       if (event_bits & (1 << j))
2139         change->has_event[j] = TRUE;
2140
2141     change->target_element = getMappedElement(getFile16BitBE(file));
2142
2143     change->delay_fixed = getFile16BitBE(file);
2144     change->delay_random = getFile16BitBE(file);
2145     change->delay_frames = getFile16BitBE(file);
2146
2147     change->trigger_element = getMappedElement(getFile16BitBE(file));
2148
2149     change->explode = getFile8Bit(file);
2150     change->use_target_content = getFile8Bit(file);
2151     change->only_if_complete = getFile8Bit(file);
2152     change->use_random_replace = getFile8Bit(file);
2153
2154     change->random_percentage = getFile8Bit(file);
2155     change->replace_when = getFile8Bit(file);
2156
2157     for (y = 0; y < 3; y++)
2158       for (x = 0; x < 3; x++)
2159         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2160
2161     change->can_change = getFile8Bit(file);
2162
2163     change->trigger_side = getFile8Bit(file);
2164
2165     change->trigger_player = getFile8Bit(file);
2166     change->trigger_page = getFile8Bit(file);
2167
2168     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2169                             CH_PAGE_ANY : (1 << change->trigger_page));
2170
2171     change->has_action = getFile8Bit(file);
2172     change->action_type = getFile8Bit(file);
2173     change->action_mode = getFile8Bit(file);
2174     change->action_arg = getFile16BitBE(file);
2175
2176     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2177     event_bits = getFile8Bit(file);
2178     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2179       if (event_bits & (1 << (j - 32)))
2180         change->has_event[j] = TRUE;
2181   }
2182
2183   /* mark this custom element as modified */
2184   ei->modified_settings = TRUE;
2185
2186   return chunk_size;
2187 }
2188
2189 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
2190 {
2191   struct ElementInfo *ei;
2192   struct ElementGroupInfo *group;
2193   int element;
2194   int i;
2195
2196   element = getFile16BitBE(file);
2197
2198   if (!IS_GROUP_ELEMENT(element))
2199   {
2200     Error(ERR_WARN, "invalid group element number %d", element);
2201
2202     ReadUnusedBytesFromFile(file, chunk_size - 2);
2203     return chunk_size;
2204   }
2205
2206   ei = &element_info[element];
2207
2208   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2209     ei->description[i] = getFile8Bit(file);
2210   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2211
2212   group = element_info[element].group;
2213
2214   group->num_elements = getFile8Bit(file);
2215
2216   ei->use_gfx_element = getFile8Bit(file);
2217   ei->gfx_element = getMappedElement(getFile16BitBE(file));
2218
2219   group->choice_mode = getFile8Bit(file);
2220
2221   /* some free bytes for future values and padding */
2222   ReadUnusedBytesFromFile(file, 3);
2223
2224   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2225     group->element[i] = getMappedElement(getFile16BitBE(file));
2226
2227   /* mark this group element as modified */
2228   element_info[element].modified_settings = TRUE;
2229
2230   return chunk_size;
2231 }
2232
2233 static int LoadLevel_MicroChunk(FILE *file, struct ElementFileConfig *config,
2234                                 int element)
2235 {
2236   int micro_chunk_size = 0;
2237   int conf_type = getFile8Bit(file);
2238   int byte_mask = conf_type & CONF_MASK_BYTES;
2239   boolean element_found = FALSE;
2240   int i;
2241
2242   micro_chunk_size += 1;
2243
2244   if (byte_mask == CONF_MASK_MULTI_BYTES)
2245   {
2246     int num_bytes = getFile16BitBE(file);
2247     byte *buffer = checked_malloc(num_bytes);
2248
2249 #if 0
2250     printf("::: - found multi bytes\n");
2251 #endif
2252
2253     ReadBytesFromFile(file, buffer, num_bytes);
2254
2255     for (i = 0; config[i].data_type != -1; i++)
2256     {
2257       if (config[i].element == element &&
2258           config[i].conf_type == conf_type)
2259       {
2260         int data_type = config[i].data_type;
2261         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2262         int max_num_entities = config[i].max_num_entities;
2263
2264         if (num_entities > max_num_entities)
2265         {
2266           Error(ERR_WARN,
2267                 "truncating number of entities for element %d from %d to %d",
2268                 element, num_entities, max_num_entities);
2269
2270           num_entities = max_num_entities;
2271         }
2272
2273         *(int *)(config[i].num_entities) = num_entities;
2274
2275         element_found = TRUE;
2276
2277         if (data_type == TYPE_STRING)
2278         {
2279           char *string = (char *)(config[i].value);
2280           int j;
2281
2282           for (j = 0; j < max_num_entities; j++)
2283             string[j] = (j < num_entities ? buffer[j] : '\0');
2284         }
2285         else if (data_type == TYPE_ELEMENT_LIST)
2286         {
2287           int *element_array = (int *)(config[i].value);
2288           int j;
2289
2290           for (j = 0; j < num_entities; j++)
2291             element_array[j] =
2292               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2293         }
2294         else if (data_type == TYPE_CONTENT_LIST)
2295         {
2296           struct Content *content= (struct Content *)(config[i].value);
2297           int c, x, y;
2298
2299           for (c = 0; c < num_entities; c++)
2300             for (y = 0; y < 3; y++)
2301               for (x = 0; x < 3; x++)
2302                 content[c].e[x][y] =
2303                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2304         }
2305         else
2306           element_found = FALSE;
2307
2308         break;
2309       }
2310     }
2311
2312     checked_free(buffer);
2313
2314     micro_chunk_size += 2 + num_bytes;
2315   }
2316   else          /* constant size configuration data (1, 2 or 4 bytes) */
2317   {
2318     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
2319                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2320                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2321
2322 #if 0
2323     printf("::: - found single bytes\n");
2324 #endif
2325
2326     for (i = 0; config[i].data_type != -1; i++)
2327     {
2328       if (config[i].element == element &&
2329           config[i].conf_type == conf_type)
2330       {
2331         int data_type = config[i].data_type;
2332
2333         if (data_type == TYPE_BOOLEAN)
2334           *(boolean *)(config[i].value) = value;
2335         else
2336           *(int *)    (config[i].value) = value;
2337
2338         element_found = TRUE;
2339
2340         break;
2341       }
2342     }
2343
2344     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2345   }
2346
2347   if (!element_found)
2348     Error(ERR_WARN, "cannot load micro chunk value for element %d", element);
2349
2350   return micro_chunk_size;
2351 }
2352
2353 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
2354 {
2355   int real_chunk_size = 0;
2356
2357 #if 1
2358   li = *level;          /* copy level data into temporary buffer */
2359 #endif
2360
2361   while (!feof(file))
2362   {
2363     int element = getFile16BitBE(file);
2364 #if 1
2365     real_chunk_size += 2;
2366     real_chunk_size += LoadLevel_MicroChunk(file, element_conf, element);
2367 #else
2368     int conf_type = getFile8Bit(file);
2369     int byte_mask = conf_type & CONF_MASK_BYTES;
2370     boolean element_found = FALSE;
2371     int i;
2372
2373     real_chunk_size += 3;
2374
2375 #if 0
2376     li = *level;        /* copy level data into temporary buffer */
2377 #endif
2378
2379     if (byte_mask == CONF_MASK_MULTI_BYTES)
2380     {
2381       int num_bytes = getFile16BitBE(file);
2382       byte *buffer = checked_malloc(num_bytes);
2383
2384       ReadBytesFromFile(file, buffer, num_bytes);
2385
2386       for (i = 0; element_conf[i].data_type != -1; i++)
2387       {
2388         if (element_conf[i].element == element &&
2389             element_conf[i].conf_type == conf_type)
2390         {
2391           int data_type = element_conf[i].data_type;
2392           int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2393           int max_num_entities = element_conf[i].max_num_entities;
2394
2395           if (num_entities > max_num_entities)
2396           {
2397             Error(ERR_WARN,
2398                   "truncating number of entities for element %d from %d to %d",
2399                   element, num_entities, max_num_entities);
2400
2401             num_entities = max_num_entities;
2402           }
2403
2404           *(int *)(element_conf[i].num_entities) = num_entities;
2405
2406           element_found = TRUE;
2407
2408           if (data_type == TYPE_ELEMENT_LIST)
2409           {
2410             int *element_array = (int *)(element_conf[i].value);
2411             int j;
2412
2413             for (j = 0; j < num_entities; j++)
2414               element_array[j] =
2415                 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2416           }
2417           else if (data_type == TYPE_CONTENT_LIST)
2418           {
2419             struct Content *content= (struct Content *)(element_conf[i].value);
2420             int c, x, y;
2421
2422             for (c = 0; c < num_entities; c++)
2423               for (y = 0; y < 3; y++)
2424                 for (x = 0; x < 3; x++)
2425                   content[c].e[x][y] =
2426                     getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2427           }
2428           else
2429             element_found = FALSE;
2430
2431           break;
2432         }
2433       }
2434
2435       checked_free(buffer);
2436
2437       real_chunk_size += 2 + num_bytes;
2438     }
2439     else        /* constant size configuration data (1, 2 or 4 bytes) */
2440     {
2441       int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
2442                    byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2443                    byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2444
2445       for (i = 0; element_conf[i].data_type != -1; i++)
2446       {
2447         if (element_conf[i].element == element &&
2448             element_conf[i].conf_type == conf_type)
2449         {
2450           int data_type = element_conf[i].data_type;
2451
2452           if (data_type == TYPE_BOOLEAN)
2453             *(boolean *)(element_conf[i].value) = value;
2454           else
2455             *(int *)    (element_conf[i].value) = value;
2456
2457           element_found = TRUE;
2458
2459           break;
2460         }
2461       }
2462
2463       real_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2464     }
2465
2466     if (!element_found)
2467       Error(ERR_WARN, "cannot load CONF value for element %d", element);
2468 #endif
2469
2470 #if 0
2471     *level = li;        /* copy temporary buffer back to level data */
2472 #endif
2473
2474 #if 1
2475     if (real_chunk_size >= chunk_size)
2476       break;
2477 #else
2478     if (conf_type == CONF_LAST_ENTRY || real_chunk_size >= chunk_size)
2479       break;
2480 #endif
2481   }
2482
2483 #if 1
2484   *level = li;          /* copy temporary buffer back to level data */
2485 #endif
2486
2487   return real_chunk_size;
2488 }
2489
2490 static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
2491 {
2492   int element = getFile16BitBE(file);
2493   int real_chunk_size = 2;
2494   struct ElementInfo *ei = &element_info[element];
2495
2496 #if 1
2497   printf("::: CUSX: loading element '%s' ...\n", EL_NAME(element));
2498 #endif
2499
2500   xx_ei = *ei;          /* copy element data into temporary buffer */
2501
2502   xx_ei.num_change_pages = -1;
2503
2504   while (!feof(file))
2505   {
2506     real_chunk_size += LoadLevel_MicroChunk(file, custom_element_conf, -1);
2507
2508 #if 0
2509     printf("::: - real_chunk_size now %d\n", real_chunk_size);
2510 #endif
2511
2512     if (xx_ei.num_change_pages != -1)
2513       break;
2514
2515     if (real_chunk_size >= chunk_size)
2516       break;
2517   }
2518
2519   *ei = xx_ei;
2520
2521   if (ei->num_change_pages == -1)
2522   {
2523     Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
2524           EL_NAME(element));
2525
2526     ei->num_change_pages = 1;
2527     setElementChangePages(ei, 1);
2528
2529     return real_chunk_size;
2530   }
2531
2532   setElementChangePages(ei, ei->num_change_pages);
2533
2534   xx_current_change_page = 0;
2535
2536   xx_event_bits_0_31 = 0;
2537   xx_event_bits_32_63 = 0;
2538
2539   while (!feof(file))
2540   {
2541     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
2542     int i;
2543
2544     xx_change = *change;        /* copy change data into temporary buffer */
2545
2546     real_chunk_size += LoadLevel_MicroChunk(file,custom_element_change_conf,-1);
2547
2548     *change = xx_change;
2549
2550     for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2551       if ((i <  32 && xx_event_bits_0_31  & (1 << i)) ||
2552           (i >= 32 && xx_event_bits_32_63 & (1 << (i - 32))))
2553         change->has_event[i] = TRUE;
2554
2555     xx_event_bits_0_31 = 0;
2556     xx_event_bits_32_63 = 0;
2557
2558     if (real_chunk_size >= chunk_size)
2559       break;
2560   }
2561
2562   return real_chunk_size;
2563 }
2564
2565 static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
2566 {
2567   int element = getFile16BitBE(file);
2568   int real_chunk_size = 2;
2569   struct ElementInfo *ei = &element_info[element];
2570   struct ElementGroupInfo *group = ei->group;
2571
2572   xx_ei = *ei;          /* copy element data into temporary buffer */
2573   xx_group = *group;    /* copy group data into temporary buffer */
2574
2575   while (!feof(file))
2576   {
2577     real_chunk_size += LoadLevel_MicroChunk(file, group_element_conf, -1);
2578
2579     if (real_chunk_size >= chunk_size)
2580       break;
2581   }
2582
2583   *ei = xx_ei;
2584   *group = xx_group;
2585
2586   return real_chunk_size;
2587 }
2588
2589 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
2590                                       struct LevelFileInfo *level_file_info)
2591 {
2592   char *filename = level_file_info->filename;
2593   char cookie[MAX_LINE_LEN];
2594   char chunk_name[CHUNK_ID_LEN + 1];
2595   int chunk_size;
2596   FILE *file;
2597
2598   if (!(file = fopen(filename, MODE_READ)))
2599   {
2600     level->no_valid_file = TRUE;
2601
2602     if (level != &level_template)
2603       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
2604
2605     return;
2606   }
2607
2608   getFileChunkBE(file, chunk_name, NULL);
2609   if (strEqual(chunk_name, "RND1"))
2610   {
2611     getFile32BitBE(file);               /* not used */
2612
2613     getFileChunkBE(file, chunk_name, NULL);
2614     if (!strEqual(chunk_name, "CAVE"))
2615     {
2616       level->no_valid_file = TRUE;
2617
2618       Error(ERR_WARN, "unknown format of level file '%s'", filename);
2619       fclose(file);
2620       return;
2621     }
2622   }
2623   else  /* check for pre-2.0 file format with cookie string */
2624   {
2625     strcpy(cookie, chunk_name);
2626     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2627     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2628       cookie[strlen(cookie) - 1] = '\0';
2629
2630     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
2631     {
2632       level->no_valid_file = TRUE;
2633
2634       Error(ERR_WARN, "unknown format of level file '%s'", filename);
2635       fclose(file);
2636       return;
2637     }
2638
2639     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
2640     {
2641       level->no_valid_file = TRUE;
2642
2643       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
2644       fclose(file);
2645       return;
2646     }
2647
2648     /* pre-2.0 level files have no game version, so use file version here */
2649     level->game_version = level->file_version;
2650   }
2651
2652   if (level->file_version < FILE_VERSION_1_2)
2653   {
2654     /* level files from versions before 1.2.0 without chunk structure */
2655     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,             level);
2656     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
2657   }
2658   else
2659   {
2660     static struct
2661     {
2662       char *name;
2663       int size;
2664       int (*loader)(FILE *, int, struct LevelInfo *);
2665     }
2666     chunk_info[] =
2667     {
2668       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
2669       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
2670       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
2671       { "BODY", -1,                     LoadLevel_BODY },
2672       { "CONT", -1,                     LoadLevel_CONT },
2673       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
2674       { "CNT3", -1,                     LoadLevel_CNT3 },
2675       { "CUS1", -1,                     LoadLevel_CUS1 },
2676       { "CUS2", -1,                     LoadLevel_CUS2 },
2677       { "CUS3", -1,                     LoadLevel_CUS3 },
2678       { "CUS4", -1,                     LoadLevel_CUS4 },
2679       { "GRP1", -1,                     LoadLevel_GRP1 },
2680       { "CONF", -1,                     LoadLevel_CONF },
2681 #if 1
2682       { "CUSX", -1,                     LoadLevel_CUSX },
2683       { "GRPX", -1,                     LoadLevel_GRPX },
2684 #endif
2685
2686       {  NULL,  0,                      NULL }
2687     };
2688
2689     while (getFileChunkBE(file, chunk_name, &chunk_size))
2690     {
2691       int i = 0;
2692
2693       while (chunk_info[i].name != NULL &&
2694              !strEqual(chunk_name, chunk_info[i].name))
2695         i++;
2696
2697       if (chunk_info[i].name == NULL)
2698       {
2699         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
2700               chunk_name, filename);
2701         ReadUnusedBytesFromFile(file, chunk_size);
2702       }
2703       else if (chunk_info[i].size != -1 &&
2704                chunk_info[i].size != chunk_size)
2705       {
2706         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
2707               chunk_size, chunk_name, filename);
2708         ReadUnusedBytesFromFile(file, chunk_size);
2709       }
2710       else
2711       {
2712         /* call function to load this level chunk */
2713         int chunk_size_expected =
2714           (chunk_info[i].loader)(file, chunk_size, level);
2715
2716         /* the size of some chunks cannot be checked before reading other
2717            chunks first (like "HEAD" and "BODY") that contain some header
2718            information, so check them here */
2719         if (chunk_size_expected != chunk_size)
2720         {
2721           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
2722                 chunk_size, chunk_name, filename);
2723         }
2724       }
2725     }
2726   }
2727
2728   fclose(file);
2729 }
2730
2731 /* ------------------------------------------------------------------------- */
2732 /* functions for loading EM level                                            */
2733 /* ------------------------------------------------------------------------- */
2734
2735 #if 0
2736
2737 static int map_em_element_yam(int element)
2738 {
2739   switch (element)
2740   {
2741     case 0x00:  return EL_EMPTY;
2742     case 0x01:  return EL_EMERALD;
2743     case 0x02:  return EL_DIAMOND;
2744     case 0x03:  return EL_ROCK;
2745     case 0x04:  return EL_ROBOT;
2746     case 0x05:  return EL_SPACESHIP_UP;
2747     case 0x06:  return EL_BOMB;
2748     case 0x07:  return EL_BUG_UP;
2749     case 0x08:  return EL_AMOEBA_DROP;
2750     case 0x09:  return EL_NUT;
2751     case 0x0a:  return EL_YAMYAM;
2752     case 0x0b:  return EL_QUICKSAND_FULL;
2753     case 0x0c:  return EL_SAND;
2754     case 0x0d:  return EL_WALL_SLIPPERY;
2755     case 0x0e:  return EL_STEELWALL;
2756     case 0x0f:  return EL_WALL;
2757     case 0x10:  return EL_EM_KEY_1;
2758     case 0x11:  return EL_EM_KEY_2;
2759     case 0x12:  return EL_EM_KEY_4;
2760     case 0x13:  return EL_EM_KEY_3;
2761     case 0x14:  return EL_MAGIC_WALL;
2762     case 0x15:  return EL_ROBOT_WHEEL;
2763     case 0x16:  return EL_DYNAMITE;
2764
2765     case 0x17:  return EL_EM_KEY_1;                     /* EMC */
2766     case 0x18:  return EL_BUG_UP;                       /* EMC */
2767     case 0x1a:  return EL_DIAMOND;                      /* EMC */
2768     case 0x1b:  return EL_EMERALD;                      /* EMC */
2769     case 0x25:  return EL_NUT;                          /* EMC */
2770     case 0x80:  return EL_EMPTY;                        /* EMC */
2771     case 0x85:  return EL_EM_KEY_1;                     /* EMC */
2772     case 0x86:  return EL_EM_KEY_2;                     /* EMC */
2773     case 0x87:  return EL_EM_KEY_4;                     /* EMC */
2774     case 0x88:  return EL_EM_KEY_3;                     /* EMC */
2775     case 0x94:  return EL_QUICKSAND_EMPTY;              /* EMC */
2776     case 0x9a:  return EL_AMOEBA_WET;                   /* EMC */
2777     case 0xaf:  return EL_DYNAMITE;                     /* EMC */
2778     case 0xbd:  return EL_SAND;                         /* EMC */
2779
2780     default:
2781       Error(ERR_WARN, "invalid level element %d", element);
2782       return EL_UNKNOWN;
2783   }
2784 }
2785
2786 static int map_em_element_field(int element)
2787 {
2788   if (element >= 0xc8 && element <= 0xe1)
2789     return EL_CHAR_A + (element - 0xc8);
2790   else if (element >= 0xe2 && element <= 0xeb)
2791     return EL_CHAR_0 + (element - 0xe2);
2792
2793   switch (element)
2794   {
2795     case 0x00:  return EL_ROCK;
2796     case 0x01:  return EL_ROCK;                         /* EMC */
2797     case 0x02:  return EL_DIAMOND;
2798     case 0x03:  return EL_DIAMOND;
2799     case 0x04:  return EL_ROBOT;
2800     case 0x05:  return EL_ROBOT;                        /* EMC */
2801     case 0x06:  return EL_EMPTY_SPACE;                  /* EMC */
2802     case 0x07:  return EL_EMPTY_SPACE;                  /* EMC */
2803     case 0x08:  return EL_SPACESHIP_UP;
2804     case 0x09:  return EL_SPACESHIP_RIGHT;
2805     case 0x0a:  return EL_SPACESHIP_DOWN;
2806     case 0x0b:  return EL_SPACESHIP_LEFT;
2807     case 0x0c:  return EL_SPACESHIP_UP;
2808     case 0x0d:  return EL_SPACESHIP_RIGHT;
2809     case 0x0e:  return EL_SPACESHIP_DOWN;
2810     case 0x0f:  return EL_SPACESHIP_LEFT;
2811
2812     case 0x10:  return EL_BOMB;
2813     case 0x11:  return EL_BOMB;                         /* EMC */
2814     case 0x12:  return EL_EMERALD;
2815     case 0x13:  return EL_EMERALD;
2816     case 0x14:  return EL_BUG_UP;
2817     case 0x15:  return EL_BUG_RIGHT;
2818     case 0x16:  return EL_BUG_DOWN;
2819     case 0x17:  return EL_BUG_LEFT;
2820     case 0x18:  return EL_BUG_UP;
2821     case 0x19:  return EL_BUG_RIGHT;
2822     case 0x1a:  return EL_BUG_DOWN;
2823     case 0x1b:  return EL_BUG_LEFT;
2824     case 0x1c:  return EL_AMOEBA_DROP;
2825     case 0x1d:  return EL_AMOEBA_DROP;                  /* EMC */
2826     case 0x1e:  return EL_AMOEBA_DROP;                  /* EMC */
2827     case 0x1f:  return EL_AMOEBA_DROP;                  /* EMC */
2828
2829     case 0x20:  return EL_ROCK;
2830     case 0x21:  return EL_BOMB;                         /* EMC */
2831     case 0x22:  return EL_DIAMOND;                      /* EMC */
2832     case 0x23:  return EL_EMERALD;                      /* EMC */
2833     case 0x24:  return EL_MAGIC_WALL;
2834     case 0x25:  return EL_NUT;
2835     case 0x26:  return EL_NUT;                          /* EMC */
2836     case 0x27:  return EL_NUT;                          /* EMC */
2837
2838       /* looks like magic wheel, but is _always_ activated */
2839     case 0x28:  return EL_ROBOT_WHEEL;                  /* EMC */
2840
2841     case 0x29:  return EL_YAMYAM;       /* up */
2842     case 0x2a:  return EL_YAMYAM;       /* down */
2843     case 0x2b:  return EL_YAMYAM;       /* left */      /* EMC */
2844     case 0x2c:  return EL_YAMYAM;       /* right */     /* EMC */
2845     case 0x2d:  return EL_QUICKSAND_FULL;
2846     case 0x2e:  return EL_EMPTY_SPACE;                  /* EMC */
2847     case 0x2f:  return EL_EMPTY_SPACE;                  /* EMC */
2848
2849     case 0x30:  return EL_EMPTY_SPACE;                  /* EMC */
2850     case 0x31:  return EL_SAND;                         /* EMC */
2851     case 0x32:  return EL_SAND;                         /* EMC */
2852     case 0x33:  return EL_SAND;                         /* EMC */
2853     case 0x34:  return EL_QUICKSAND_FULL;               /* EMC */
2854     case 0x35:  return EL_QUICKSAND_FULL;               /* EMC */
2855     case 0x36:  return EL_QUICKSAND_FULL;               /* EMC */
2856     case 0x37:  return EL_SAND;                         /* EMC */
2857     case 0x38:  return EL_ROCK;                         /* EMC */
2858     case 0x39:  return EL_EXPANDABLE_WALL_HORIZONTAL;   /* EMC */
2859     case 0x3a:  return EL_EXPANDABLE_WALL_VERTICAL;     /* EMC */
2860     case 0x3b:  return EL_DYNAMITE_ACTIVE;      /* 1 */
2861     case 0x3c:  return EL_DYNAMITE_ACTIVE;      /* 2 */
2862     case 0x3d:  return EL_DYNAMITE_ACTIVE;      /* 3 */
2863     case 0x3e:  return EL_DYNAMITE_ACTIVE;      /* 4 */
2864     case 0x3f:  return EL_ACID_POOL_BOTTOM;
2865
2866     case 0x40:  return EL_EXIT_OPEN;    /* 1 */
2867     case 0x41:  return EL_EXIT_OPEN;    /* 2 */
2868     case 0x42:  return EL_EXIT_OPEN;    /* 3 */
2869     case 0x43:  return EL_BALLOON;                      /* EMC */
2870     case 0x44:  return EL_UNKNOWN;                      /* EMC ("plant") */
2871     case 0x45:  return EL_SPRING;                       /* EMC */
2872     case 0x46:  return EL_SPRING;       /* falling */   /* EMC */
2873     case 0x47:  return EL_SPRING;       /* left */      /* EMC */
2874     case 0x48:  return EL_SPRING;       /* right */     /* EMC */
2875     case 0x49:  return EL_UNKNOWN;                      /* EMC ("ball 1") */
2876     case 0x4a:  return EL_UNKNOWN;                      /* EMC ("ball 2") */
2877     case 0x4b:  return EL_UNKNOWN;                      /* EMC ("android") */
2878     case 0x4c:  return EL_EMPTY_SPACE;                  /* EMC */
2879     case 0x4d:  return EL_UNKNOWN;                      /* EMC ("android") */
2880     case 0x4e:  return EL_INVISIBLE_WALL;               /* EMC (? "android") */
2881     case 0x4f:  return EL_UNKNOWN;                      /* EMC ("android") */
2882
2883     case 0x50:  return EL_UNKNOWN;                      /* EMC ("android") */
2884     case 0x51:  return EL_UNKNOWN;                      /* EMC ("android") */
2885     case 0x52:  return EL_UNKNOWN;                      /* EMC ("android") */
2886     case 0x53:  return EL_UNKNOWN;                      /* EMC ("android") */
2887     case 0x54:  return EL_UNKNOWN;                      /* EMC ("android") */
2888     case 0x55:  return EL_EMPTY_SPACE;                  /* EMC */
2889     case 0x56:  return EL_EMPTY_SPACE;                  /* EMC */
2890     case 0x57:  return EL_EMPTY_SPACE;                  /* EMC */
2891     case 0x58:  return EL_EMPTY_SPACE;                  /* EMC */
2892     case 0x59:  return EL_EMPTY_SPACE;                  /* EMC */
2893     case 0x5a:  return EL_EMPTY_SPACE;                  /* EMC */
2894     case 0x5b:  return EL_EMPTY_SPACE;                  /* EMC */
2895     case 0x5c:  return EL_EMPTY_SPACE;                  /* EMC */
2896     case 0x5d:  return EL_EMPTY_SPACE;                  /* EMC */
2897     case 0x5e:  return EL_EMPTY_SPACE;                  /* EMC */
2898     case 0x5f:  return EL_EMPTY_SPACE;                  /* EMC */
2899
2900     case 0x60:  return EL_EMPTY_SPACE;                  /* EMC */
2901     case 0x61:  return EL_EMPTY_SPACE;                  /* EMC */
2902     case 0x62:  return EL_EMPTY_SPACE;                  /* EMC */
2903     case 0x63:  return EL_SPRING;       /* left */      /* EMC */
2904     case 0x64:  return EL_SPRING;       /* right */     /* EMC */
2905     case 0x65:  return EL_ACID;         /* 1 */         /* EMC */
2906     case 0x66:  return EL_ACID;         /* 2 */         /* EMC */
2907     case 0x67:  return EL_ACID;         /* 3 */         /* EMC */
2908     case 0x68:  return EL_ACID;         /* 4 */         /* EMC */
2909     case 0x69:  return EL_ACID;         /* 5 */         /* EMC */
2910     case 0x6a:  return EL_ACID;         /* 6 */         /* EMC */
2911     case 0x6b:  return EL_ACID;         /* 7 */         /* EMC */
2912     case 0x6c:  return EL_ACID;         /* 8 */         /* EMC */
2913     case 0x6d:  return EL_EMPTY_SPACE;                  /* EMC */
2914     case 0x6e:  return EL_EMPTY_SPACE;                  /* EMC */
2915     case 0x6f:  return EL_EMPTY_SPACE;                  /* EMC */
2916
2917     case 0x70:  return EL_EMPTY_SPACE;                  /* EMC */
2918     case 0x71:  return EL_EMPTY_SPACE;                  /* EMC */
2919     case 0x72:  return EL_NUT;          /* left */      /* EMC */
2920     case 0x73:  return EL_SAND;                         /* EMC (? "nut") */
2921     case 0x74:  return EL_STEELWALL;
2922     case 0x75:  return EL_EMPTY_SPACE;                  /* EMC */
2923     case 0x76:  return EL_EMPTY_SPACE;                  /* EMC */
2924     case 0x77:  return EL_BOMB;         /* left */      /* EMC */
2925     case 0x78:  return EL_BOMB;         /* right */     /* EMC */
2926     case 0x79:  return EL_ROCK;         /* left */      /* EMC */
2927     case 0x7a:  return EL_ROCK;         /* right */     /* EMC */
2928     case 0x7b:  return EL_ACID;                         /* (? EMC "blank") */
2929     case 0x7c:  return EL_EMPTY_SPACE;                  /* EMC */
2930     case 0x7d:  return EL_EMPTY_SPACE;                  /* EMC */
2931     case 0x7e:  return EL_EMPTY_SPACE;                  /* EMC */
2932     case 0x7f:  return EL_EMPTY_SPACE;                  /* EMC */
2933
2934     case 0x80:  return EL_EMPTY;
2935     case 0x81:  return EL_WALL_SLIPPERY;
2936     case 0x82:  return EL_SAND;
2937     case 0x83:  return EL_STEELWALL;
2938     case 0x84:  return EL_WALL;
2939     case 0x85:  return EL_EM_KEY_1;
2940     case 0x86:  return EL_EM_KEY_2;
2941     case 0x87:  return EL_EM_KEY_4;
2942     case 0x88:  return EL_EM_KEY_3;
2943     case 0x89:  return EL_EM_GATE_1;
2944     case 0x8a:  return EL_EM_GATE_2;
2945     case 0x8b:  return EL_EM_GATE_4;
2946     case 0x8c:  return EL_EM_GATE_3;
2947     case 0x8d:  return EL_INVISIBLE_WALL;               /* EMC (? "dripper") */
2948     case 0x8e:  return EL_EM_GATE_1_GRAY;
2949     case 0x8f:  return EL_EM_GATE_2_GRAY;
2950
2951     case 0x90:  return EL_EM_GATE_4_GRAY;
2952     case 0x91:  return EL_EM_GATE_3_GRAY;
2953     case 0x92:  return EL_MAGIC_WALL;
2954     case 0x93:  return EL_ROBOT_WHEEL;
2955     case 0x94:  return EL_QUICKSAND_EMPTY;              /* (? EMC "sand") */
2956     case 0x95:  return EL_ACID_POOL_TOPLEFT;
2957     case 0x96:  return EL_ACID_POOL_TOPRIGHT;
2958     case 0x97:  return EL_ACID_POOL_BOTTOMLEFT;
2959     case 0x98:  return EL_ACID_POOL_BOTTOMRIGHT;
2960     case 0x99:  return EL_ACID;                 /* (? EMC "fake blank") */
2961     case 0x9a:  return EL_AMOEBA_DEAD;          /* 1 */
2962     case 0x9b:  return EL_AMOEBA_DEAD;          /* 2 */
2963     case 0x9c:  return EL_AMOEBA_DEAD;          /* 3 */
2964     case 0x9d:  return EL_AMOEBA_DEAD;          /* 4 */
2965     case 0x9e:  return EL_EXIT_CLOSED;
2966     case 0x9f:  return EL_CHAR_LESS;            /* arrow left */
2967
2968       /* looks like normal sand, but behaves like wall */
2969     case 0xa0:  return EL_UNKNOWN;              /* EMC ("fake grass") */
2970     case 0xa1:  return EL_UNKNOWN;              /* EMC ("lenses") */
2971     case 0xa2:  return EL_UNKNOWN;              /* EMC ("magnify") */
2972     case 0xa3:  return EL_UNKNOWN;              /* EMC ("fake blank") */
2973     case 0xa4:  return EL_UNKNOWN;              /* EMC ("fake grass") */
2974     case 0xa5:  return EL_UNKNOWN;              /* EMC ("switch") */
2975     case 0xa6:  return EL_UNKNOWN;              /* EMC ("switch") */
2976     case 0xa7:  return EL_EMPTY_SPACE;                  /* EMC */
2977     case 0xa8:  return EL_EMC_WALL_1;                   /* EMC ("decor 8") */
2978     case 0xa9:  return EL_EMC_WALL_2;                   /* EMC ("decor 9") */
2979     case 0xaa:  return EL_EMC_WALL_3;                   /* EMC ("decor 10") */
2980     case 0xab:  return EL_EMC_WALL_7;                   /* EMC ("decor 5") */
2981     case 0xac:  return EL_CHAR_COMMA;                   /* EMC */
2982     case 0xad:  return EL_CHAR_QUOTEDBL;                /* EMC */
2983     case 0xae:  return EL_CHAR_MINUS;                   /* EMC */
2984     case 0xaf:  return EL_DYNAMITE;
2985
2986     case 0xb0:  return EL_EMC_STEELWALL_1;              /* EMC ("steel 3") */
2987     case 0xb1:  return EL_EMC_WALL_8;                   /* EMC ("decor 6") */
2988     case 0xb2:  return EL_UNKNOWN;                      /* EMC ("decor 7") */
2989     case 0xb3:  return EL_STEELWALL;            /* 2 */ /* EMC */
2990     case 0xb4:  return EL_WALL_SLIPPERY;        /* 2 */ /* EMC */
2991     case 0xb5:  return EL_EMC_WALL_6;                   /* EMC ("decor 2") */
2992     case 0xb6:  return EL_EMC_WALL_5;                   /* EMC ("decor 4") */
2993     case 0xb7:  return EL_EMC_WALL_4;                   /* EMC ("decor 3") */
2994     case 0xb8:  return EL_BALLOON_SWITCH_ANY;           /* EMC */
2995     case 0xb9:  return EL_BALLOON_SWITCH_RIGHT;         /* EMC */
2996     case 0xba:  return EL_BALLOON_SWITCH_DOWN;          /* EMC */
2997     case 0xbb:  return EL_BALLOON_SWITCH_LEFT;          /* EMC */
2998     case 0xbc:  return EL_BALLOON_SWITCH_UP;            /* EMC */
2999     case 0xbd:  return EL_SAND;                         /* EMC ("dirt") */
3000     case 0xbe:  return EL_UNKNOWN;                      /* EMC ("plant") */
3001     case 0xbf:  return EL_UNKNOWN;                      /* EMC ("key 5") */
3002
3003     case 0xc0:  return EL_UNKNOWN;                      /* EMC ("key 6") */
3004     case 0xc1:  return EL_UNKNOWN;                      /* EMC ("key 7") */
3005     case 0xc2:  return EL_UNKNOWN;                      /* EMC ("key 8") */
3006     case 0xc3:  return EL_UNKNOWN;                      /* EMC ("door 5") */
3007     case 0xc4:  return EL_UNKNOWN;                      /* EMC ("door 6") */
3008     case 0xc5:  return EL_UNKNOWN;                      /* EMC ("door 7") */
3009     case 0xc6:  return EL_UNKNOWN;                      /* EMC ("door 8") */
3010     case 0xc7:  return EL_UNKNOWN;                      /* EMC ("bumper") */
3011
3012       /* characters: see above */
3013
3014     case 0xec:  return EL_CHAR_PERIOD;
3015     case 0xed:  return EL_CHAR_EXCLAM;
3016     case 0xee:  return EL_CHAR_COLON;
3017     case 0xef:  return EL_CHAR_QUESTION;
3018
3019     case 0xf0:  return EL_CHAR_GREATER;                 /* arrow right */
3020     case 0xf1:  return EL_CHAR_COPYRIGHT;               /* EMC: "decor 1" */
3021     case 0xf2:  return EL_UNKNOWN;              /* EMC ("fake door 5") */
3022     case 0xf3:  return EL_UNKNOWN;              /* EMC ("fake door 6") */
3023     case 0xf4:  return EL_UNKNOWN;              /* EMC ("fake door 7") */
3024     case 0xf5:  return EL_UNKNOWN;              /* EMC ("fake door 8") */
3025     case 0xf6:  return EL_EMPTY_SPACE;                  /* EMC */
3026     case 0xf7:  return EL_EMPTY_SPACE;                  /* EMC */
3027
3028     case 0xf8:  return EL_EMPTY_SPACE;                  /* EMC */
3029     case 0xf9:  return EL_EMPTY_SPACE;                  /* EMC */
3030     case 0xfa:  return EL_EMPTY_SPACE;                  /* EMC */
3031     case 0xfb:  return EL_EMPTY_SPACE;                  /* EMC */
3032     case 0xfc:  return EL_EMPTY_SPACE;                  /* EMC */
3033     case 0xfd:  return EL_EMPTY_SPACE;                  /* EMC */
3034
3035     case 0xfe:  return EL_PLAYER_1;                     /* EMC: "blank" */
3036     case 0xff:  return EL_PLAYER_2;                     /* EMC: "blank" */
3037
3038     default:
3039       /* should never happen (all 8-bit value cases should be handled) */
3040       Error(ERR_WARN, "invalid level element %d", element);
3041       return EL_UNKNOWN;
3042   }
3043 }
3044
3045 #define EM_LEVEL_SIZE                   2106
3046 #define EM_LEVEL_XSIZE                  64
3047 #define EM_LEVEL_YSIZE                  32
3048
3049 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
3050                                          struct LevelFileInfo *level_file_info)
3051 {
3052   char *filename = level_file_info->filename;
3053   FILE *file;
3054   unsigned char leveldata[EM_LEVEL_SIZE];
3055   unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
3056   int nr = level_file_info->nr;
3057   int i, x, y;
3058
3059   if (!(file = fopen(filename, MODE_READ)))
3060   {
3061     level->no_valid_file = TRUE;
3062
3063     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3064
3065     return;
3066   }
3067
3068   for (i = 0; i < EM_LEVEL_SIZE; i++)
3069     leveldata[i] = fgetc(file);
3070
3071   fclose(file);
3072
3073   /* check if level data is crypted by testing against known starting bytes
3074      of the few existing crypted level files (from Emerald Mine 1 + 2) */
3075
3076   if ((leveldata[0] == 0xf1 ||
3077        leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
3078   {
3079     unsigned char code0 = 0x65;
3080     unsigned char code1 = 0x11;
3081
3082     if (leveldata[0] == 0xf5)   /* error in crypted Emerald Mine 2 levels */
3083       leveldata[0] = 0xf1;
3084
3085     /* decode crypted level data */
3086
3087     for (i = 0; i < EM_LEVEL_SIZE; i++)
3088     {
3089       leveldata[i] ^= code0;
3090       leveldata[i] -= code1;
3091
3092       code0 = (code0 + 7) & 0xff;
3093     }
3094   }
3095
3096   level->fieldx = EM_LEVEL_XSIZE;
3097   level->fieldy = EM_LEVEL_YSIZE;
3098
3099   level->time           = header[46] * 10;
3100   level->gems_needed    = header[47];
3101
3102   /* The original Emerald Mine levels have their level number stored
3103      at the second byte of the level file...
3104      Do not trust this information at other level files, e.g. EMC,
3105      but correct it anyway (normally the first row is completely
3106      steel wall, so the correction does not hurt anyway). */
3107
3108   if (leveldata[1] == nr)
3109     leveldata[1] = leveldata[2];        /* correct level number field */
3110
3111   sprintf(level->name, "Level %d", nr);         /* set level name */
3112
3113   level->score[SC_EMERALD]      = header[36];
3114   level->score[SC_DIAMOND]      = header[37];
3115   level->score[SC_ROBOT]        = header[38];
3116   level->score[SC_SPACESHIP]    = header[39];
3117   level->score[SC_BUG]          = header[40];
3118   level->score[SC_YAMYAM]       = header[41];
3119   level->score[SC_NUT]          = header[42];
3120   level->score[SC_DYNAMITE]     = header[43];
3121   level->score[SC_TIME_BONUS]   = header[44];
3122
3123   level->num_yamyam_contents = 4;
3124
3125   for (i = 0; i < level->num_yamyam_contents; i++)
3126     for (y = 0; y < 3; y++)
3127       for (x = 0; x < 3; x++)
3128         level->yamyam_content[i].e[x][y] =
3129           map_em_element_yam(header[i * 9 + y * 3 + x]);
3130
3131   level->amoeba_speed           = (header[52] * 256 + header[53]) % 256;
3132   level->time_magic_wall        = (header[54] * 256 + header[55]) * 16 / 100;
3133   level->time_wheel             = (header[56] * 256 + header[57]) * 16 / 100;
3134   level->amoeba_content         = EL_DIAMOND;
3135
3136   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3137   {
3138     int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
3139
3140     if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
3141       new_element = EL_AMOEBA_WET;
3142
3143     level->field[x][y] = new_element;
3144   }
3145
3146   x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
3147   y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
3148   level->field[x][y] = EL_PLAYER_1;
3149
3150   x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
3151   y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
3152   level->field[x][y] = EL_PLAYER_2;
3153 }
3154
3155 #endif
3156
3157 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3158 {
3159   static int ball_xy[8][2] =
3160   {
3161     { 0, 0 },
3162     { 1, 0 },
3163     { 2, 0 },
3164     { 0, 1 },
3165     { 2, 1 },
3166     { 0, 2 },
3167     { 1, 2 },
3168     { 2, 2 },
3169   };
3170   struct LevelInfo_EM *level_em = level->native_em_level;
3171   struct LEVEL *lev = level_em->lev;
3172   struct PLAYER **ply = level_em->ply;
3173   int i, j, x, y;
3174
3175 #if 0
3176   printf("::: A\n");
3177   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3178     for (j = 0; j < 8; j++)
3179       printf("::: ball %d, %d: %d\n", i, j,
3180              level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3181 #endif
3182
3183   lev->width  = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3184   lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3185
3186   lev->time_seconds     = level->time;
3187   lev->required_initial = level->gems_needed;
3188
3189   lev->emerald_score    = level->score[SC_EMERALD];
3190   lev->diamond_score    = level->score[SC_DIAMOND];
3191   lev->alien_score      = level->score[SC_ROBOT];
3192   lev->tank_score       = level->score[SC_SPACESHIP];
3193   lev->bug_score        = level->score[SC_BUG];
3194   lev->eater_score      = level->score[SC_YAMYAM];
3195   lev->nut_score        = level->score[SC_NUT];
3196   lev->dynamite_score   = level->score[SC_DYNAMITE];
3197   lev->key_score        = level->score[SC_KEY];
3198   lev->exit_score       = level->score[SC_TIME_BONUS];
3199
3200   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3201     for (y = 0; y < 3; y++)
3202       for (x = 0; x < 3; x++)
3203         lev->eater_array[i][y * 3 + x] =
3204           map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3205
3206   lev->amoeba_time              = level->amoeba_speed;
3207   lev->wonderwall_time_initial  = level->time_magic_wall;
3208   lev->wheel_time               = level->time_wheel;
3209
3210   lev->android_move_time        = level->android_move_time;
3211   lev->android_clone_time       = level->android_clone_time;
3212   lev->ball_random              = level->ball_random;
3213   lev->ball_state_initial       = level->ball_state_initial;
3214   lev->ball_time                = level->ball_time;
3215   lev->num_ball_arrays          = level->num_ball_contents;
3216
3217   lev->lenses_score             = level->lenses_score;
3218   lev->magnify_score            = level->magnify_score;
3219   lev->slurp_score              = level->slurp_score;
3220
3221   lev->lenses_time              = level->lenses_time;
3222   lev->magnify_time             = level->magnify_time;
3223
3224   lev->wind_direction_initial =
3225     map_direction_RND_to_EM(level->wind_direction_initial);
3226   lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3227                            lev->wind_time : 0);
3228
3229   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3230     for (j = 0; j < 8; j++)
3231       lev->ball_array[i][j] =
3232         map_element_RND_to_EM(level->
3233                               ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3234
3235 #if 0
3236   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3237     for (j = 0; j < 8; j++)
3238       printf("::: ball %d, %d: %d\n", i, j,
3239              level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3240 #endif
3241
3242   map_android_clone_elements_RND_to_EM(level);
3243
3244 #if 0
3245   for (i = 0; i < 16; i++)
3246     lev->android_array[i] = FALSE;      /* !!! YET TO COME !!! */
3247 #endif
3248
3249   /* first fill the complete playfield with the default border element */
3250   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3251     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3252       level_em->cave[x][y] = ZBORDER;
3253
3254 #if 1
3255
3256 #if 0
3257 #if 1
3258   LoadLevel_InitPlayfield();
3259 #else
3260   lev_fieldx = lev->width;      /* !!! also in LoadLevel_InitPlayfield() !!! */
3261   lev_fieldy = lev->height;     /* !!! also in LoadLevel_InitPlayfield() !!! */
3262   SetBorderElement();           /* !!! also in LoadLevel_InitPlayfield() !!! */
3263 #endif
3264 #endif
3265
3266 #if 0
3267   printf("::: BorderElement == %d\n", BorderElement);
3268 #endif
3269
3270   if (BorderElement == EL_STEELWALL)
3271   {
3272     for (y = 0; y < lev->height + 2; y++)
3273       for (x = 0; x < lev->width + 2; x++)
3274         level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3275   }
3276
3277   /* then copy the real level contents from level file into the playfield */
3278   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3279   {
3280     int new_element = map_element_RND_to_EM(level->field[x][y]);
3281     int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3282     int xx = x + 1 + offset;
3283     int yy = y + 1 + offset;
3284
3285     if (level->field[x][y] == EL_AMOEBA_DEAD)
3286       new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3287
3288     level_em->cave[xx][yy] = new_element;
3289   }
3290
3291 #else
3292
3293   /* then copy the real level contents from level file into the playfield */
3294   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3295   {
3296     int new_element = map_element_RND_to_EM(level->field[x][y]);
3297
3298     if (level->field[x][y] == EL_AMOEBA_DEAD)
3299       new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3300
3301     level_em->cave[x + 1][y + 1] = new_element;
3302   }
3303
3304 #endif
3305
3306 #if 1
3307
3308   for (i = 0; i < MAX_PLAYERS; i++)
3309   {
3310     ply[i]->x_initial = 0;
3311     ply[i]->y_initial = 0;
3312   }
3313
3314 #else
3315
3316   ply1->x_initial = 0;
3317   ply1->y_initial = 0;
3318
3319   ply2->x_initial = 0;
3320   ply2->y_initial = 0;
3321
3322 #endif
3323
3324   /* initialize player positions and delete players from the playfield */
3325   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3326   {
3327
3328 #if 1
3329     if (ELEM_IS_PLAYER(level->field[x][y]))
3330     {
3331       int player_nr = GET_PLAYER_NR(level->field[x][y]);
3332       int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3333       int xx = x + 1 + offset;
3334       int yy = y + 1 + offset;
3335
3336       ply[player_nr]->x_initial = xx;
3337       ply[player_nr]->y_initial = yy;
3338
3339       level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3340     }
3341
3342 #else
3343
3344 #if 1
3345     /* !!! CURRENTLY ONLY SUPPORT FOR ONE PLAYER !!! */
3346     if (ELEM_IS_PLAYER(level->field[x][y]))
3347     {
3348       ply1->x_initial = x + 1;
3349       ply1->y_initial = y + 1;
3350       level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
3351     }
3352 #else
3353     /* !!! ADD SUPPORT FOR MORE THAN ONE PLAYER !!! */
3354     if (level->field[x][y] == EL_PLAYER_1)
3355     {
3356       ply1->x_initial = x + 1;
3357       ply1->y_initial = y + 1;
3358       level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
3359     }
3360     else if (level->field[x][y] == EL_PLAYER_2)
3361     {
3362       ply2->x_initial = x + 1;
3363       ply2->y_initial = y + 1;
3364       level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
3365     }
3366 #endif
3367
3368 #endif
3369
3370   }
3371
3372   if (BorderElement == EL_STEELWALL)
3373   {
3374 #if 1
3375     lev->width  += 2;
3376     lev->height += 2;
3377 #endif
3378   }
3379 }
3380
3381 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3382 {
3383   static int ball_xy[8][2] =
3384   {
3385     { 0, 0 },
3386     { 1, 0 },
3387     { 2, 0 },
3388     { 0, 1 },
3389     { 2, 1 },
3390     { 0, 2 },
3391     { 1, 2 },
3392     { 2, 2 },
3393   };
3394   struct LevelInfo_EM *level_em = level->native_em_level;
3395   struct LEVEL *lev = level_em->lev;
3396   struct PLAYER **ply = level_em->ply;
3397   int i, j, x, y;
3398
3399   level->fieldx = MIN(lev->width,  MAX_LEV_FIELDX);
3400   level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3401
3402   level->time        = lev->time_seconds;
3403   level->gems_needed = lev->required_initial;
3404
3405   sprintf(level->name, "Level %d", level->file_info.nr);
3406
3407   level->score[SC_EMERALD]      = lev->emerald_score;
3408   level->score[SC_DIAMOND]      = lev->diamond_score;
3409   level->score[SC_ROBOT]        = lev->alien_score;
3410   level->score[SC_SPACESHIP]    = lev->tank_score;
3411   level->score[SC_BUG]          = lev->bug_score;
3412   level->score[SC_YAMYAM]       = lev->eater_score;
3413   level->score[SC_NUT]          = lev->nut_score;
3414   level->score[SC_DYNAMITE]     = lev->dynamite_score;
3415   level->score[SC_KEY]          = lev->key_score;
3416   level->score[SC_TIME_BONUS]   = lev->exit_score;
3417
3418   level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3419
3420   for (i = 0; i < level->num_yamyam_contents; i++)
3421     for (y = 0; y < 3; y++)
3422       for (x = 0; x < 3; x++)
3423         level->yamyam_content[i].e[x][y] =
3424           map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3425
3426   level->amoeba_speed           = lev->amoeba_time;
3427   level->time_magic_wall        = lev->wonderwall_time_initial;
3428   level->time_wheel             = lev->wheel_time;
3429
3430   level->android_move_time      = lev->android_move_time;
3431   level->android_clone_time     = lev->android_clone_time;
3432   level->ball_random            = lev->ball_random;
3433   level->ball_state_initial     = lev->ball_state_initial;
3434   level->ball_time              = lev->ball_time;
3435   level->num_ball_contents      = lev->num_ball_arrays;
3436
3437   level->lenses_score           = lev->lenses_score;
3438   level->magnify_score          = lev->magnify_score;
3439   level->slurp_score            = lev->slurp_score;
3440
3441   level->lenses_time            = lev->lenses_time;
3442   level->magnify_time           = lev->magnify_time;
3443
3444   level->wind_direction_initial =
3445     map_direction_EM_to_RND(lev->wind_direction_initial);
3446
3447 #if 0
3448   printf("::: foo\n");
3449   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3450     for (j = 0; j < 8; j++)
3451       printf("::: ball %d, %d: %d\n", i, j,
3452              level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3453 #endif
3454
3455   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3456     for (j = 0; j < 8; j++)
3457       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3458         map_element_EM_to_RND(lev->ball_array[i][j]);
3459
3460 #if 0
3461   printf("::: bar\n");
3462   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3463     for (j = 0; j < 8; j++)
3464       printf("::: ball %d, %d: %d\n", i, j,
3465              level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3466 #endif
3467
3468   map_android_clone_elements_EM_to_RND(level);
3469
3470 #if 0
3471   for (i = 0; i < 16; i++)
3472     level->android_array[i] = FALSE;    /* !!! YET TO COME !!! */
3473 #endif
3474
3475   /* convert the playfield (some elements need special treatment) */
3476   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3477   {
3478     int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3479
3480     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3481       new_element = EL_AMOEBA_DEAD;
3482
3483     level->field[x][y] = new_element;
3484   }
3485
3486 #if 0
3487   printf("::: bar 0\n");
3488   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3489     for (j = 0; j < 8; j++)
3490       printf("::: ball %d, %d: %d\n", i, j,
3491              level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3492 #endif
3493
3494 #if 1
3495
3496   for (i = 0; i < MAX_PLAYERS; i++)
3497   {
3498     /* in case of all players set to the same field, use the first player */
3499     int nr = MAX_PLAYERS - i - 1;
3500     int jx = ply[nr]->x_initial - 1;
3501     int jy = ply[nr]->y_initial - 1;
3502
3503 #if 0
3504     printf("::: player %d: %d, %d\n", nr, jx, jy);
3505 #endif
3506
3507     if (jx != -1 && jy != -1)
3508       level->field[jx][jy] = EL_PLAYER_1 + nr;
3509   }
3510
3511 #else
3512
3513   /* in case of both players set to the same field, use the first player */
3514   level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
3515   level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
3516
3517 #endif
3518
3519 #if 0
3520   printf("::: native Emerald Mine file version: %d\n", level_em->file_version);
3521 #endif
3522
3523 #if 0
3524   printf("::: bar 2\n");
3525   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3526     for (j = 0; j < 8; j++)
3527       printf("::: ball %d, %d: %d\n", i, j,
3528              level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3529 #endif
3530 }
3531
3532 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
3533                                      struct LevelFileInfo *level_file_info)
3534 {
3535   if (!LoadNativeLevel_EM(level_file_info->filename))
3536     level->no_valid_file = TRUE;
3537 }
3538
3539 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
3540 {
3541   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
3542     CopyNativeLevel_RND_to_EM(level);
3543 }
3544
3545 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
3546 {
3547
3548 #if 0
3549   {
3550     static int ball_xy[8][2] =
3551       {
3552         { 0, 0 },
3553         { 1, 0 },
3554         { 2, 0 },
3555         { 0, 1 },
3556         { 2, 1 },
3557         { 0, 2 },
3558         { 1, 2 },
3559         { 2, 2 },
3560       };
3561     int i, j;
3562
3563     printf("::: A6\n");
3564     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3565       for (j = 0; j < 8; j++)
3566         printf("::: ball %d, %d: %d\n", i, j,
3567                level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3568   }
3569 #endif
3570
3571   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
3572     CopyNativeLevel_EM_to_RND(level);
3573 }
3574
3575
3576 /* ------------------------------------------------------------------------- */
3577 /* functions for loading SP level                                            */
3578 /* ------------------------------------------------------------------------- */
3579
3580 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
3581 #define SP_LEVEL_SIZE                   1536
3582 #define SP_LEVEL_XSIZE                  60
3583 #define SP_LEVEL_YSIZE                  24
3584 #define SP_LEVEL_NAME_LEN               23
3585
3586 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
3587                                        int nr)
3588 {
3589   int num_special_ports;
3590   int i, x, y;
3591
3592   /* for details of the Supaplex level format, see Herman Perk's Supaplex
3593      documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
3594
3595   /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
3596   for (y = 0; y < SP_LEVEL_YSIZE; y++)
3597   {
3598     for (x = 0; x < SP_LEVEL_XSIZE; x++)
3599     {
3600       int element_old = fgetc(file);
3601       int element_new;
3602
3603       if (element_old <= 0x27)
3604         element_new = getMappedElement(EL_SP_START + element_old);
3605       else if (element_old == 0x28)
3606         element_new = EL_INVISIBLE_WALL;
3607       else
3608       {
3609         Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
3610         Error(ERR_WARN, "invalid level element %d", element_old);
3611
3612         element_new = EL_UNKNOWN;
3613       }
3614
3615       level->field[x][y] = element_new;
3616     }
3617   }
3618
3619   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
3620
3621   /* initial gravity: 1 == "on", anything else (0) == "off" */
3622   level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
3623
3624   ReadUnusedBytesFromFile(file, 1);     /* (not used by Supaplex engine) */
3625
3626   /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
3627   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3628     level->name[i] = fgetc(file);
3629   level->name[SP_LEVEL_NAME_LEN] = '\0';
3630
3631   /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
3632   ReadUnusedBytesFromFile(file, 1);     /* (not used by R'n'D engine) */
3633
3634   /* number of infotrons needed; 0 means that Supaplex will count the total
3635      amount of infotrons in the level and use the low byte of that number
3636      (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
3637   level->gems_needed = fgetc(file);
3638
3639   /* number of special ("gravity") port entries below (maximum 10 allowed) */
3640   num_special_ports = fgetc(file);
3641
3642   /* database of properties of up to 10 special ports (6 bytes per port) */
3643   for (i = 0; i < 10; i++)
3644   {
3645     int port_location, port_x, port_y, port_element;
3646     int gravity;
3647
3648     /* high and low byte of the location of a special port; if (x, y) are the
3649        coordinates of a port in the field and (0, 0) is the top-left corner,
3650        the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
3651        of what may be expected: Supaplex works with a game field in memory
3652        which is 2 bytes per tile) */
3653     port_location = getFile16BitBE(file);
3654
3655     /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
3656     gravity = fgetc(file);
3657
3658     /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
3659     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
3660
3661     /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
3662     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
3663
3664     ReadUnusedBytesFromFile(file, 1);   /* (not used by Supaplex engine) */
3665
3666     if (i >= num_special_ports)
3667       continue;
3668
3669     port_x = (port_location / 2) % SP_LEVEL_XSIZE;
3670     port_y = (port_location / 2) / SP_LEVEL_XSIZE;
3671
3672     if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
3673         port_y < 0 || port_y >= SP_LEVEL_YSIZE)
3674     {
3675       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3676             port_x, port_y);
3677
3678       continue;
3679     }
3680
3681     port_element = level->field[port_x][port_y];
3682
3683     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3684         port_element > EL_SP_GRAVITY_PORT_UP)
3685     {
3686       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3687
3688       continue;
3689     }
3690
3691     /* change previous (wrong) gravity inverting special port to either
3692        gravity enabling special port or gravity disabling special port */
3693     level->field[port_x][port_y] +=
3694       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3695        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3696   }
3697
3698   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
3699
3700   /* change special gravity ports without database entries to normal ports */
3701   for (y = 0; y < SP_LEVEL_YSIZE; y++)
3702     for (x = 0; x < SP_LEVEL_XSIZE; x++)
3703       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3704           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3705         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3706
3707   /* auto-determine number of infotrons if it was stored as "0" -- see above */
3708   if (level->gems_needed == 0)
3709   {
3710     for (y = 0; y < SP_LEVEL_YSIZE; y++)
3711       for (x = 0; x < SP_LEVEL_XSIZE; x++)
3712         if (level->field[x][y] == EL_SP_INFOTRON)
3713           level->gems_needed++;
3714
3715     level->gems_needed &= 0xff;         /* only use low byte -- see above */
3716   }
3717
3718   level->fieldx = SP_LEVEL_XSIZE;
3719   level->fieldy = SP_LEVEL_YSIZE;
3720
3721   level->time = 0;                      /* no time limit */
3722   level->amoeba_speed = 0;
3723   level->time_magic_wall = 0;
3724   level->time_wheel = 0;
3725   level->amoeba_content = EL_EMPTY;
3726
3727 #if 1
3728   /* original Supaplex does not use score values -- use default values */
3729 #else
3730   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3731     level->score[i] = 0;                /* !!! CORRECT THIS !!! */
3732 #endif
3733
3734   /* there are no yamyams in supaplex levels */
3735   for (i = 0; i < level->num_yamyam_contents; i++)
3736     for (y = 0; y < 3; y++)
3737       for (x = 0; x < 3; x++)
3738         level->yamyam_content[i].e[x][y] = EL_EMPTY;
3739 }
3740
3741 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
3742                                      struct LevelFileInfo *level_file_info)
3743 {
3744   char *filename = level_file_info->filename;
3745   FILE *file;
3746   int nr = level_file_info->nr - leveldir_current->first_level;
3747   int i, l, x, y;
3748   char name_first, name_last;
3749   struct LevelInfo multipart_level;
3750   int multipart_xpos, multipart_ypos;
3751   boolean is_multipart_level;
3752   boolean is_first_part;
3753   boolean reading_multipart_level = FALSE;
3754   boolean use_empty_level = FALSE;
3755
3756   if (!(file = fopen(filename, MODE_READ)))
3757   {
3758     level->no_valid_file = TRUE;
3759
3760     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3761
3762     return;
3763   }
3764
3765   /* position file stream to the requested level inside the level package */
3766   if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
3767   {
3768     level->no_valid_file = TRUE;
3769
3770     Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
3771
3772     return;
3773   }
3774
3775   /* there exist Supaplex level package files with multi-part levels which
3776      can be detected as follows: instead of leading and trailing dashes ('-')
3777      to pad the level name, they have leading and trailing numbers which are
3778      the x and y coordinations of the current part of the multi-part level;
3779      if there are '?' characters instead of numbers on the left or right side
3780      of the level name, the multi-part level consists of only horizontal or
3781      vertical parts */
3782
3783   for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
3784   {
3785     LoadLevelFromFileStream_SP(file, level, l);
3786
3787     /* check if this level is a part of a bigger multi-part level */
3788
3789     name_first = level->name[0];
3790     name_last  = level->name[SP_LEVEL_NAME_LEN - 1];
3791
3792     is_multipart_level =
3793       ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
3794        (name_last  == '?' || (name_last  >= '0' && name_last  <= '9')));
3795
3796     is_first_part =
3797       ((name_first == '?' || name_first == '1') &&
3798        (name_last  == '?' || name_last  == '1'));
3799
3800     /* correct leading multipart level meta information in level name */
3801     for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
3802       level->name[i] = '-';
3803
3804     /* correct trailing multipart level meta information in level name */
3805     for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
3806       level->name[i] = '-';
3807
3808     /* ---------- check for normal single level ---------- */
3809
3810     if (!reading_multipart_level && !is_multipart_level)
3811     {
3812       /* the current level is simply a normal single-part level, and we are
3813          not reading a multi-part level yet, so return the level as it is */
3814
3815       break;
3816     }
3817
3818     /* ---------- check for empty level (unused multi-part) ---------- */
3819
3820     if (!reading_multipart_level && is_multipart_level && !is_first_part)
3821     {
3822       /* this is a part of a multi-part level, but not the first part
3823          (and we are not already reading parts of a multi-part level);
3824          in this case, use an empty level instead of the single part */
3825
3826       use_empty_level = TRUE;
3827
3828       break;
3829     }
3830
3831     /* ---------- check for finished multi-part level ---------- */
3832
3833     if (reading_multipart_level &&
3834         (!is_multipart_level ||
3835          !strEqual(level->name, multipart_level.name)))
3836     {
3837       /* we are already reading parts of a multi-part level, but this level is
3838          either not a multi-part level, or a part of a different multi-part
3839          level; in both cases, the multi-part level seems to be complete */
3840
3841       break;
3842     }
3843
3844     /* ---------- here we have one part of a multi-part level ---------- */
3845
3846     reading_multipart_level = TRUE;
3847
3848     if (is_first_part)  /* start with first part of new multi-part level */
3849     {
3850       /* copy level info structure from first part */
3851       multipart_level = *level;
3852
3853       /* clear playfield of new multi-part level */
3854       for (y = 0; y < MAX_LEV_FIELDY; y++)
3855         for (x = 0; x < MAX_LEV_FIELDX; x++)
3856           multipart_level.field[x][y] = EL_EMPTY;
3857     }
3858
3859     if (name_first == '?')
3860       name_first = '1';
3861     if (name_last == '?')
3862       name_last = '1';
3863
3864     multipart_xpos = (int)(name_first - '0');
3865     multipart_ypos = (int)(name_last  - '0');
3866
3867 #if 0
3868     printf("----------> part (%d/%d) of multi-part level '%s'\n",
3869            multipart_xpos, multipart_ypos, multipart_level.name);
3870 #endif
3871
3872     if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
3873         multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
3874     {
3875       Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
3876
3877       break;
3878     }
3879
3880     multipart_level.fieldx = MAX(multipart_level.fieldx,
3881                                  multipart_xpos * SP_LEVEL_XSIZE);
3882     multipart_level.fieldy = MAX(multipart_level.fieldy,
3883                                  multipart_ypos * SP_LEVEL_YSIZE);
3884
3885     /* copy level part at the right position of multi-part level */
3886     for (y = 0; y < SP_LEVEL_YSIZE; y++)
3887     {
3888       for (x = 0; x < SP_LEVEL_XSIZE; x++)
3889       {
3890         int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
3891         int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
3892
3893         multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
3894       }
3895     }
3896   }
3897
3898   fclose(file);
3899
3900   if (use_empty_level)
3901   {
3902     setLevelInfoToDefaults(level);
3903
3904     level->fieldx = SP_LEVEL_XSIZE;
3905     level->fieldy = SP_LEVEL_YSIZE;
3906
3907     for (y = 0; y < SP_LEVEL_YSIZE; y++)
3908       for (x = 0; x < SP_LEVEL_XSIZE; x++)
3909         level->field[x][y] = EL_EMPTY;
3910
3911     strcpy(level->name, "-------- EMPTY --------");
3912
3913     Error(ERR_WARN, "single part of multi-part level -- using empty level");
3914   }
3915
3916   if (reading_multipart_level)
3917     *level = multipart_level;
3918 }
3919
3920 /* ------------------------------------------------------------------------- */
3921 /* functions for loading generic level                                       */
3922 /* ------------------------------------------------------------------------- */
3923
3924 void LoadLevelFromFileInfo(struct LevelInfo *level,
3925                            struct LevelFileInfo *level_file_info)
3926 {
3927   /* always start with reliable default values */
3928   setLevelInfoToDefaults(level);
3929
3930   switch (level_file_info->type)
3931   {
3932     case LEVEL_FILE_TYPE_RND:
3933       LoadLevelFromFileInfo_RND(level, level_file_info);
3934       break;
3935
3936     case LEVEL_FILE_TYPE_EM:
3937       LoadLevelFromFileInfo_EM(level, level_file_info);
3938       level->game_engine_type = GAME_ENGINE_TYPE_EM;
3939       break;
3940
3941     case LEVEL_FILE_TYPE_SP:
3942       LoadLevelFromFileInfo_SP(level, level_file_info);
3943       break;
3944
3945     default:
3946       LoadLevelFromFileInfo_RND(level, level_file_info);
3947       break;
3948   }
3949
3950   /* if level file is invalid, restore level structure to default values */
3951   if (level->no_valid_file)
3952     setLevelInfoToDefaults(level);
3953
3954   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
3955     level->game_engine_type = GAME_ENGINE_TYPE_RND;
3956
3957 #if 1
3958   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
3959     CopyNativeLevel_Native_to_RND(level);
3960 #else
3961   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
3962     CopyNativeLevel_RND_to_Native(level);
3963   else
3964     CopyNativeLevel_Native_to_RND(level);
3965 #endif
3966 }
3967
3968 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
3969 {
3970   static struct LevelFileInfo level_file_info;
3971
3972   /* always start with reliable default values */
3973   setFileInfoToDefaults(&level_file_info);
3974
3975   level_file_info.nr = 0;                       /* unknown level number */
3976   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
3977   level_file_info.filename = filename;
3978
3979   LoadLevelFromFileInfo(level, &level_file_info);
3980 }
3981
3982 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
3983 {
3984   if (leveldir_current == NULL)         /* only when dumping level */
3985     return;
3986
3987   /* all engine modifications also valid for levels which use latest engine */
3988 #if 1
3989   if (level->game_version < VERSION_IDENT(3,2,0,5))
3990   {
3991     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
3992     level->score[SC_TIME_BONUS] /= 10;
3993   }
3994 #endif
3995
3996 #if 0
3997   leveldir_current->latest_engine = TRUE;       /* !!! TEST ONLY !!! */
3998 #endif
3999
4000   if (leveldir_current->latest_engine)
4001   {
4002     /* ---------- use latest game engine ----------------------------------- */
4003
4004     /* For all levels which are forced to use the latest game engine version
4005        (normally all but user contributed, private and undefined levels), set
4006        the game engine version to the actual version; this allows for actual
4007        corrections in the game engine to take effect for existing, converted
4008        levels (from "classic" or other existing games) to make the emulation
4009        of the corresponding game more accurate, while (hopefully) not breaking
4010        existing levels created from other players. */
4011
4012     level->game_version = GAME_VERSION_ACTUAL;
4013
4014     /* Set special EM style gems behaviour: EM style gems slip down from
4015        normal, steel and growing wall. As this is a more fundamental change,
4016        it seems better to set the default behaviour to "off" (as it is more
4017        natural) and make it configurable in the level editor (as a property
4018        of gem style elements). Already existing converted levels (neither
4019        private nor contributed levels) are changed to the new behaviour. */
4020
4021     if (level->file_version < FILE_VERSION_2_0)
4022       level->em_slippery_gems = TRUE;
4023
4024     return;
4025   }
4026
4027   /* ---------- use game engine the level was created with ----------------- */
4028
4029   /* For all levels which are not forced to use the latest game engine
4030      version (normally user contributed, private and undefined levels),
4031      use the version of the game engine the levels were created for.
4032
4033      Since 2.0.1, the game engine version is now directly stored
4034      in the level file (chunk "VERS"), so there is no need anymore
4035      to set the game version from the file version (except for old,
4036      pre-2.0 levels, where the game version is still taken from the
4037      file format version used to store the level -- see above). */
4038
4039   /* player was faster than enemies in 1.0.0 and before */
4040   if (level->file_version == FILE_VERSION_1_0)
4041     level->initial_player_stepsize = STEPSIZE_FAST;
4042
4043   /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
4044   if (level->game_version == VERSION_IDENT(2,0,1,0))
4045     level->em_slippery_gems = TRUE;
4046
4047   /* springs could be pushed over pits before (pre-release version) 2.2.0 */
4048   if (level->game_version < VERSION_IDENT(2,2,0,0))
4049     level->use_spring_bug = TRUE;
4050
4051   if (level->game_version < VERSION_IDENT(3,2,0,5))
4052   {
4053     /* time orb caused limited time in endless time levels before 3.2.0-5 */
4054     level->use_time_orb_bug = TRUE;
4055
4056     /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
4057     level->block_snap_field = FALSE;
4058
4059     /* extra time score was same value as time left score before 3.2.0-5 */
4060     level->extra_time_score = level->score[SC_TIME_BONUS];
4061
4062 #if 0
4063     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
4064     level->score[SC_TIME_BONUS] /= 10;
4065 #endif
4066   }
4067
4068   if (level->game_version < VERSION_IDENT(3,2,0,7))
4069   {
4070     /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
4071     level->continuous_snapping = FALSE;
4072   }
4073
4074   /* only few elements were able to actively move into acid before 3.1.0 */
4075   /* trigger settings did not exist before 3.1.0; set to default "any" */
4076   if (level->game_version < VERSION_IDENT(3,1,0,0))
4077   {
4078     int i, j;
4079
4080     /* correct "can move into acid" settings (all zero in old levels) */
4081
4082     level->can_move_into_acid_bits = 0; /* nothing can move into acid */
4083     level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
4084
4085     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
4086     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
4087     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
4088     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
4089
4090     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4091       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
4092
4093     /* correct trigger settings (stored as zero == "none" in old levels) */
4094
4095     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4096     {
4097       int element = EL_CUSTOM_START + i;
4098       struct ElementInfo *ei = &element_info[element];
4099
4100       for (j = 0; j < ei->num_change_pages; j++)
4101       {
4102         struct ElementChangeInfo *change = &ei->change_page[j];
4103
4104         change->trigger_player = CH_PLAYER_ANY;
4105         change->trigger_page = CH_PAGE_ANY;
4106       }
4107     }
4108   }
4109 }
4110
4111 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
4112 {
4113   int i, j, x, y;
4114
4115   /* map custom element change events that have changed in newer versions
4116      (these following values were accidentally changed in version 3.0.1)
4117      (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
4118   if (level->game_version <= VERSION_IDENT(3,0,0,0))
4119   {
4120     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4121     {
4122       int element = EL_CUSTOM_START + i;
4123
4124       /* order of checking and copying events to be mapped is important */
4125       /* (do not change the start and end value -- they are constant) */
4126       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
4127       {
4128         if (HAS_CHANGE_EVENT(element, j - 2))
4129         {
4130           SET_CHANGE_EVENT(element, j - 2, FALSE);
4131           SET_CHANGE_EVENT(element, j, TRUE);
4132         }
4133       }
4134
4135       /* order of checking and copying events to be mapped is important */
4136       /* (do not change the start and end value -- they are constant) */
4137       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
4138       {
4139         if (HAS_CHANGE_EVENT(element, j - 1))
4140         {
4141           SET_CHANGE_EVENT(element, j - 1, FALSE);
4142           SET_CHANGE_EVENT(element, j, TRUE);
4143         }
4144       }
4145     }
4146   }
4147
4148   /* initialize "can_change" field for old levels with only one change page */
4149   if (level->game_version <= VERSION_IDENT(3,0,2,0))
4150   {
4151     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4152     {
4153       int element = EL_CUSTOM_START + i;
4154
4155       if (CAN_CHANGE(element))
4156         element_info[element].change->can_change = TRUE;
4157     }
4158   }
4159
4160   /* correct custom element values (for old levels without these options) */
4161   if (level->game_version < VERSION_IDENT(3,1,1,0))
4162   {
4163     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4164     {
4165       int element = EL_CUSTOM_START + i;
4166       struct ElementInfo *ei = &element_info[element];
4167
4168       if (ei->access_direction == MV_NO_DIRECTION)
4169         ei->access_direction = MV_ALL_DIRECTIONS;
4170
4171 #if 0
4172       for (j = 0; j < ei->num_change_pages; j++)
4173       {
4174         struct ElementChangeInfo *change = &ei->change_page[j];
4175
4176         if (change->trigger_side == CH_SIDE_NONE)
4177           change->trigger_side = CH_SIDE_ANY;
4178       }
4179 #endif
4180     }
4181   }
4182
4183 #if 1
4184   /* correct custom element values (fix invalid values for all versions) */
4185   if (1)
4186   {
4187     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4188     {
4189       int element = EL_CUSTOM_START + i;
4190       struct ElementInfo *ei = &element_info[element];
4191
4192       for (j = 0; j < ei->num_change_pages; j++)
4193       {
4194         struct ElementChangeInfo *change = &ei->change_page[j];
4195
4196         if (change->trigger_player == CH_PLAYER_NONE)
4197           change->trigger_player = CH_PLAYER_ANY;
4198
4199         if (change->trigger_side == CH_SIDE_NONE)
4200           change->trigger_side = CH_SIDE_ANY;
4201       }
4202     }
4203   }
4204 #endif
4205
4206   /* initialize "can_explode" field for old levels which did not store this */
4207   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
4208   if (level->game_version <= VERSION_IDENT(3,1,0,0))
4209   {
4210     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4211     {
4212       int element = EL_CUSTOM_START + i;
4213
4214       if (EXPLODES_1X1_OLD(element))
4215         element_info[element].explosion_type = EXPLODES_1X1;
4216
4217       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
4218                                              EXPLODES_SMASHED(element) ||
4219                                              EXPLODES_IMPACT(element)));
4220     }
4221   }
4222
4223   /* correct previously hard-coded move delay values for maze runner style */
4224   if (level->game_version < VERSION_IDENT(3,1,1,0))
4225   {
4226     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4227     {
4228       int element = EL_CUSTOM_START + i;
4229
4230       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
4231       {
4232         /* previously hard-coded and therefore ignored */
4233         element_info[element].move_delay_fixed = 9;
4234         element_info[element].move_delay_random = 0;
4235       }
4236     }
4237   }
4238
4239   /* map elements that have changed in newer versions */
4240   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
4241                                                     level->game_version);
4242   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4243     for (x = 0; x < 3; x++)
4244       for (y = 0; y < 3; y++)
4245         level->yamyam_content[i].e[x][y] =
4246           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
4247                                     level->game_version);
4248
4249   /* initialize element properties for level editor etc. */
4250   InitElementPropertiesEngine(level->game_version);
4251 }
4252
4253 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
4254 {
4255   int x, y;
4256
4257   /* map elements that have changed in newer versions */
4258   for (y = 0; y < level->fieldy; y++)
4259     for (x = 0; x < level->fieldx; x++)
4260       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
4261                                                      level->game_version);
4262
4263   /* copy elements to runtime playfield array */
4264   for (x = 0; x < MAX_LEV_FIELDX; x++)
4265     for (y = 0; y < MAX_LEV_FIELDY; y++)
4266       Feld[x][y] = level->field[x][y];
4267
4268   /* initialize level size variables for faster access */
4269   lev_fieldx = level->fieldx;
4270   lev_fieldy = level->fieldy;
4271
4272   /* determine border element for this level */
4273   SetBorderElement();
4274 }
4275
4276 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
4277 {
4278   struct LevelFileInfo *level_file_info = &level->file_info;
4279
4280 #if 1
4281   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
4282     CopyNativeLevel_RND_to_Native(level);
4283 #else
4284   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
4285     CopyNativeLevel_RND_to_Native(level);
4286   else
4287     CopyNativeLevel_Native_to_RND(level);
4288 #endif
4289 }
4290
4291 void LoadLevelTemplate(int nr)
4292 {
4293   char *filename;
4294
4295   setLevelFileInfo(&level_template.file_info, nr);
4296   filename = level_template.file_info.filename;
4297
4298   LoadLevelFromFileInfo(&level_template, &level_template.file_info);
4299
4300   LoadLevel_InitVersion(&level_template, filename);
4301   LoadLevel_InitElements(&level_template, filename);
4302
4303   ActivateLevelTemplate();
4304 }
4305
4306 void LoadLevel(int nr)
4307 {
4308   char *filename;
4309
4310   setLevelFileInfo(&level.file_info, nr);
4311   filename = level.file_info.filename;
4312
4313   LoadLevelFromFileInfo(&level, &level.file_info);
4314
4315   if (level.use_custom_template)
4316     LoadLevelTemplate(-1);
4317
4318   LoadLevel_InitVersion(&level, filename);
4319   LoadLevel_InitElements(&level, filename);
4320   LoadLevel_InitPlayfield(&level, filename);
4321
4322   LoadLevel_InitNativeEngines(&level, filename);
4323 }
4324
4325 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
4326 {
4327   putFileVersion(file, level->file_version);
4328   putFileVersion(file, level->game_version);
4329 }
4330
4331 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
4332 {
4333   int i, x, y;
4334
4335   putFile8Bit(file, level->fieldx);
4336   putFile8Bit(file, level->fieldy);
4337
4338   putFile16BitBE(file, level->time);
4339   putFile16BitBE(file, level->gems_needed);
4340
4341   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
4342     putFile8Bit(file, level->name[i]);
4343
4344   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4345     putFile8Bit(file, level->score[i]);
4346
4347   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
4348     for (y = 0; y < 3; y++)
4349       for (x = 0; x < 3; x++)
4350         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
4351                            level->yamyam_content[i].e[x][y]));
4352   putFile8Bit(file, level->amoeba_speed);
4353   putFile8Bit(file, level->time_magic_wall);
4354   putFile8Bit(file, level->time_wheel);
4355   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
4356                      level->amoeba_content));
4357   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
4358   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
4359   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
4360   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
4361
4362   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
4363
4364   putFile8Bit(file, (level->block_last_field ? 1 : 0));
4365   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
4366   putFile32BitBE(file, level->can_move_into_acid_bits);
4367   putFile8Bit(file, level->dont_collide_with_bits);
4368
4369   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
4370   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
4371
4372   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
4373   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
4374   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
4375
4376   putFile8Bit(file, level->game_engine_type);
4377
4378   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
4379 }
4380
4381 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
4382 {
4383   int i;
4384
4385   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
4386     putFile8Bit(file, level->author[i]);
4387 }
4388
4389 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
4390 {
4391   int x, y;
4392
4393   for (y = 0; y < level->fieldy; y++) 
4394     for (x = 0; x < level->fieldx; x++) 
4395       if (level->encoding_16bit_field)
4396         putFile16BitBE(file, level->field[x][y]);
4397       else
4398         putFile8Bit(file, level->field[x][y]);
4399 }
4400
4401 #if 0
4402 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
4403 {
4404   int i, x, y;
4405
4406   putFile8Bit(file, EL_YAMYAM);
4407   putFile8Bit(file, level->num_yamyam_contents);
4408   putFile8Bit(file, 0);
4409   putFile8Bit(file, 0);
4410
4411   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4412     for (y = 0; y < 3; y++)
4413       for (x = 0; x < 3; x++)
4414         if (level->encoding_16bit_field)
4415           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
4416         else
4417           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
4418 }
4419 #endif
4420
4421 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
4422 {
4423   int i, x, y;
4424   int num_contents, content_xsize, content_ysize;
4425   int content_array[MAX_ELEMENT_CONTENTS][3][3];
4426
4427   if (element == EL_YAMYAM)
4428   {
4429     num_contents = level->num_yamyam_contents;
4430     content_xsize = 3;
4431     content_ysize = 3;
4432
4433     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4434       for (y = 0; y < 3; y++)
4435         for (x = 0; x < 3; x++)
4436           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
4437   }
4438   else if (element == EL_BD_AMOEBA)
4439   {
4440     num_contents = 1;
4441     content_xsize = 1;
4442     content_ysize = 1;
4443
4444     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4445       for (y = 0; y < 3; y++)
4446         for (x = 0; x < 3; x++)
4447           content_array[i][x][y] = EL_EMPTY;
4448     content_array[0][0][0] = level->amoeba_content;
4449   }
4450   else
4451   {
4452     /* chunk header already written -- write empty chunk data */
4453     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
4454
4455     Error(ERR_WARN, "cannot save content for element '%d'", element);
4456     return;
4457   }
4458
4459   putFile16BitBE(file, element);
4460   putFile8Bit(file, num_contents);
4461   putFile8Bit(file, content_xsize);
4462   putFile8Bit(file, content_ysize);
4463
4464   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
4465
4466   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4467     for (y = 0; y < 3; y++)
4468       for (x = 0; x < 3; x++)
4469         putFile16BitBE(file, content_array[i][x][y]);
4470 }
4471
4472 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
4473 {
4474   int i;
4475   int envelope_nr = element - EL_ENVELOPE_1;
4476   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
4477
4478   putFile16BitBE(file, element);
4479   putFile16BitBE(file, envelope_len);
4480   putFile8Bit(file, level->envelope_xsize[envelope_nr]);
4481   putFile8Bit(file, level->envelope_ysize[envelope_nr]);
4482
4483   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
4484
4485   for (i = 0; i < envelope_len; i++)
4486     putFile8Bit(file, level->envelope_text[envelope_nr][i]);
4487 }
4488
4489 #if 0
4490 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
4491                            int num_changed_custom_elements)
4492 {
4493   int i, check = 0;
4494
4495   putFile16BitBE(file, num_changed_custom_elements);
4496
4497   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4498   {
4499     int element = EL_CUSTOM_START + i;
4500
4501 #if 1
4502     struct ElementInfo *ei = &element_info[element];
4503
4504     if (ei->properties[EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
4505     {
4506       if (check < num_changed_custom_elements)
4507       {
4508         putFile16BitBE(file, element);
4509         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE]);
4510       }
4511
4512       check++;
4513     }
4514 #else
4515     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
4516     {
4517       if (check < num_changed_custom_elements)
4518       {
4519         putFile16BitBE(file, element);
4520         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
4521       }
4522
4523       check++;
4524     }
4525 #endif
4526   }
4527
4528   if (check != num_changed_custom_elements)     /* should not happen */
4529     Error(ERR_WARN, "inconsistent number of custom element properties");
4530 }
4531 #endif
4532
4533 #if 0
4534 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
4535                            int num_changed_custom_elements)
4536 {
4537   int i, check = 0;
4538
4539   putFile16BitBE(file, num_changed_custom_elements);
4540
4541   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4542   {
4543     int element = EL_CUSTOM_START + i;
4544
4545     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
4546     {
4547       if (check < num_changed_custom_elements)
4548       {
4549         putFile16BitBE(file, element);
4550         putFile16BitBE(file, element_info[element].change->target_element);
4551       }
4552
4553       check++;
4554     }
4555   }
4556
4557   if (check != num_changed_custom_elements)     /* should not happen */
4558     Error(ERR_WARN, "inconsistent number of custom target elements");
4559 }
4560 #endif
4561
4562 #if 0
4563 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
4564                            int num_changed_custom_elements)
4565 {
4566   int i, j, x, y, check = 0;
4567
4568   putFile16BitBE(file, num_changed_custom_elements);
4569
4570   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4571   {
4572     int element = EL_CUSTOM_START + i;
4573     struct ElementInfo *ei = &element_info[element];
4574
4575     if (ei->modified_settings)
4576     {
4577       if (check < num_changed_custom_elements)
4578       {
4579         putFile16BitBE(file, element);
4580
4581         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
4582           putFile8Bit(file, ei->description[j]);
4583
4584 #if 1
4585         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE]);
4586 #else
4587         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
4588 #endif
4589
4590         /* some free bytes for future properties and padding */
4591         WriteUnusedBytesToFile(file, 7);
4592
4593         putFile8Bit(file, ei->use_gfx_element);
4594         putFile16BitBE(file, ei->gfx_element);
4595
4596         putFile8Bit(file, ei->collect_score_initial);
4597         putFile8Bit(file, ei->collect_count_initial);
4598
4599         putFile16BitBE(file, ei->push_delay_fixed);
4600         putFile16BitBE(file, ei->push_delay_random);
4601         putFile16BitBE(file, ei->move_delay_fixed);
4602         putFile16BitBE(file, ei->move_delay_random);
4603
4604         putFile16BitBE(file, ei->move_pattern);
4605         putFile8Bit(file, ei->move_direction_initial);
4606         putFile8Bit(file, ei->move_stepsize);
4607
4608         for (y = 0; y < 3; y++)
4609           for (x = 0; x < 3; x++)
4610             putFile16BitBE(file, ei->content.e[x][y]);
4611
4612         putFile32BitBE(file, ei->change->events);
4613
4614         putFile16BitBE(file, ei->change->target_element);
4615
4616         putFile16BitBE(file, ei->change->delay_fixed);
4617         putFile16BitBE(file, ei->change->delay_random);
4618         putFile16BitBE(file, ei->change->delay_frames);
4619
4620         putFile16BitBE(file, ei->change->trigger_element);
4621
4622         putFile8Bit(file, ei->change->explode);
4623         putFile8Bit(file, ei->change->use_target_content);
4624         putFile8Bit(file, ei->change->only_if_complete);
4625         putFile8Bit(file, ei->change->use_random_replace);
4626
4627         putFile8Bit(file, ei->change->random_percentage);
4628         putFile8Bit(file, ei->change->replace_when);
4629
4630         for (y = 0; y < 3; y++)
4631           for (x = 0; x < 3; x++)
4632             putFile16BitBE(file, ei->change->content.e[x][y]);
4633
4634         putFile8Bit(file, ei->slippery_type);
4635
4636         /* some free bytes for future properties and padding */
4637         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
4638       }
4639
4640       check++;
4641     }
4642   }
4643
4644   if (check != num_changed_custom_elements)     /* should not happen */
4645     Error(ERR_WARN, "inconsistent number of custom element properties");
4646 }
4647 #endif
4648
4649 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
4650 {
4651   struct ElementInfo *ei = &element_info[element];
4652   int i, j, x, y;
4653
4654   /* ---------- custom element base property values (96 bytes) ------------- */
4655
4656   putFile16BitBE(file, element);
4657
4658   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
4659     putFile8Bit(file, ei->description[i]);
4660
4661 #if 1
4662   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE]);
4663 #else
4664   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
4665 #endif
4666   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
4667
4668   putFile8Bit(file, ei->num_change_pages);
4669
4670   putFile16BitBE(file, ei->ce_value_fixed_initial);
4671   putFile16BitBE(file, ei->ce_value_random_initial);
4672   putFile8Bit(file, ei->use_last_ce_value);
4673
4674   putFile8Bit(file, ei->use_gfx_element);
4675   putFile16BitBE(file, ei->gfx_element);
4676
4677   putFile8Bit(file, ei->collect_score_initial);
4678   putFile8Bit(file, ei->collect_count_initial);
4679
4680   putFile8Bit(file, ei->drop_delay_fixed);
4681   putFile8Bit(file, ei->push_delay_fixed);
4682   putFile8Bit(file, ei->drop_delay_random);
4683   putFile8Bit(file, ei->push_delay_random);
4684   putFile16BitBE(file, ei->move_delay_fixed);
4685   putFile16BitBE(file, ei->move_delay_random);
4686
4687   /* bits 0 - 15 of "move_pattern" ... */
4688   putFile16BitBE(file, ei->move_pattern & 0xffff);
4689   putFile8Bit(file, ei->move_direction_initial);
4690   putFile8Bit(file, ei->move_stepsize);
4691
4692   putFile8Bit(file, ei->slippery_type);
4693
4694   for (y = 0; y < 3; y++)
4695     for (x = 0; x < 3; x++)
4696       putFile16BitBE(file, ei->content.e[x][y]);
4697
4698   putFile16BitBE(file, ei->move_enter_element);
4699   putFile16BitBE(file, ei->move_leave_element);
4700   putFile8Bit(file, ei->move_leave_type);
4701
4702   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
4703   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
4704
4705   putFile8Bit(file, ei->access_direction);
4706
4707   putFile8Bit(file, ei->explosion_delay);
4708   putFile8Bit(file, ei->ignition_delay);
4709   putFile8Bit(file, ei->explosion_type);
4710
4711   /* some free bytes for future custom property values and padding */
4712   WriteUnusedBytesToFile(file, 1);
4713
4714   /* ---------- change page property values (48 bytes) --------------------- */
4715
4716   for (i = 0; i < ei->num_change_pages; i++)
4717   {
4718     struct ElementChangeInfo *change = &ei->change_page[i];
4719     unsigned int event_bits;
4720
4721     /* bits 0 - 31 of "has_event[]" ... */
4722     event_bits = 0;
4723     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
4724       if (change->has_event[j])
4725         event_bits |= (1 << j);
4726     putFile32BitBE(file, event_bits);
4727
4728     putFile16BitBE(file, change->target_element);
4729
4730     putFile16BitBE(file, change->delay_fixed);
4731     putFile16BitBE(file, change->delay_random);
4732     putFile16BitBE(file, change->delay_frames);
4733
4734     putFile16BitBE(file, change->trigger_element);
4735
4736     putFile8Bit(file, change->explode);
4737     putFile8Bit(file, change->use_target_content);
4738     putFile8Bit(file, change->only_if_complete);
4739     putFile8Bit(file, change->use_random_replace);
4740
4741     putFile8Bit(file, change->random_percentage);
4742     putFile8Bit(file, change->replace_when);
4743
4744     for (y = 0; y < 3; y++)
4745       for (x = 0; x < 3; x++)
4746         putFile16BitBE(file, change->target_content.e[x][y]);
4747
4748     putFile8Bit(file, change->can_change);
4749
4750     putFile8Bit(file, change->trigger_side);
4751
4752     putFile8Bit(file, change->trigger_player);
4753     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
4754                        log_2(change->trigger_page)));
4755
4756     putFile8Bit(file, change->has_action);
4757     putFile8Bit(file, change->action_type);
4758     putFile8Bit(file, change->action_mode);
4759     putFile16BitBE(file, change->action_arg);
4760
4761     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
4762     event_bits = 0;
4763     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
4764       if (change->has_event[j])
4765         event_bits |= (1 << (j - 32));
4766     putFile8Bit(file, event_bits);
4767   }
4768 }
4769
4770 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
4771 {
4772   struct ElementInfo *ei = &element_info[element];
4773   struct ElementGroupInfo *group = ei->group;
4774   int i;
4775
4776   putFile16BitBE(file, element);
4777
4778   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
4779     putFile8Bit(file, ei->description[i]);
4780
4781   putFile8Bit(file, group->num_elements);
4782
4783   putFile8Bit(file, ei->use_gfx_element);
4784   putFile16BitBE(file, ei->gfx_element);
4785
4786   putFile8Bit(file, group->choice_mode);
4787
4788   /* some free bytes for future values and padding */
4789   WriteUnusedBytesToFile(file, 3);
4790
4791   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
4792     putFile16BitBE(file, group->element[i]);
4793 }
4794
4795 static int SaveLevel_MicroChunk(FILE *file, struct ElementFileConfig *entry)
4796 {
4797   int data_type = entry->data_type;
4798   int conf_type = entry->conf_type;
4799   int byte_mask = conf_type & CONF_MASK_BYTES;
4800   int element = entry->element;
4801   int default_value = entry->default_value;
4802   int num_bytes = 0;
4803   boolean modified = FALSE;
4804
4805   if (byte_mask != CONF_MASK_MULTI_BYTES)
4806   {
4807     void *value_ptr = entry->value;
4808     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
4809                  *(int *)value_ptr);
4810
4811     /* check if any settings have been modified before saving them */
4812     if (value != default_value)
4813       modified = TRUE;
4814
4815     if (!modified)              /* do not save unmodified default settings */
4816       return 0;
4817
4818     if (element != -1)
4819       num_bytes += putFile16BitBE(file, element);
4820
4821     num_bytes += putFile8Bit(file, conf_type);
4822     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
4823                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
4824                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
4825                   0);
4826
4827     return num_bytes;
4828   }
4829   else if (data_type == TYPE_STRING)
4830   {
4831     char *default_string = entry->default_string;
4832     char *string = (char *)(entry->value);
4833     int string_length = strlen(string);
4834     int i;
4835
4836     /* check if any settings have been modified before saving them */
4837     if (!strEqual(string, default_string))
4838       modified = TRUE;
4839
4840     if (!modified)              /* do not save unmodified default settings */
4841       return 0;
4842
4843     if (element != -1)
4844       num_bytes += putFile16BitBE(file, element);
4845
4846     num_bytes += putFile8Bit(file, conf_type);
4847     num_bytes += putFile16BitBE(file, string_length);
4848
4849     for (i = 0; i < string_length; i++)
4850       num_bytes += putFile8Bit(file, string[i]);
4851
4852     return num_bytes;
4853   }
4854   else if (data_type == TYPE_ELEMENT_LIST)
4855   {
4856     int *element_array = (int *)(entry->value);
4857     int num_elements = *(int *)(entry->num_entities);
4858     int i;
4859
4860     /* check if any settings have been modified before saving them */
4861     for (i = 0; i < num_elements; i++)
4862       if (element_array[i] != default_value)
4863         modified = TRUE;
4864
4865     if (!modified)              /* do not save unmodified default settings */
4866       return 0;
4867
4868     if (element != -1)
4869       num_bytes += putFile16BitBE(file, element);
4870
4871     num_bytes += putFile8Bit(file, conf_type);
4872     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
4873
4874     for (i = 0; i < num_elements; i++)
4875       num_bytes += putFile16BitBE(file, element_array[i]);
4876
4877     return num_bytes;
4878   }
4879   else if (data_type == TYPE_CONTENT_LIST)
4880   {
4881     struct Content *content = (struct Content *)(entry->value);
4882     int num_contents = *(int *)(entry->num_entities);
4883     int i, x, y;
4884
4885     /* check if any settings have been modified before saving them */
4886     for (i = 0; i < num_contents; i++)
4887       for (y = 0; y < 3; y++)
4888         for (x = 0; x < 3; x++)
4889           if (content[i].e[x][y] != default_value)
4890             modified = TRUE;
4891
4892     if (!modified)              /* do not save unmodified default settings */
4893       return 0;
4894
4895     if (element != -1)
4896       num_bytes += putFile16BitBE(file, element);
4897
4898     num_bytes += putFile8Bit(file, conf_type);
4899     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
4900
4901     for (i = 0; i < num_contents; i++)
4902       for (y = 0; y < 3; y++)
4903         for (x = 0; x < 3; x++)
4904           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
4905
4906     return num_bytes;
4907   }
4908
4909   return 0;
4910 }
4911
4912 #if 0
4913
4914 static int SaveLevel_MicroChunk_SingleValue(FILE *file,
4915                                             struct ElementFileConfig *entry)
4916 {
4917   int default_value = entry->default_value;
4918   int element = entry->element;
4919   int data_type = entry->data_type;
4920   int conf_type = entry->conf_type;
4921   int byte_mask = conf_type & CONF_MASK_BYTES;
4922   void *value_ptr = entry->value;
4923   int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
4924                *(int *)value_ptr);
4925   int num_bytes = 0;
4926   boolean modified = FALSE;
4927
4928   /* check if any settings have been modified before saving them */
4929   if (value != default_value)
4930     modified = TRUE;
4931
4932   if (!modified)                /* do not save unmodified default settings */
4933     return 0;
4934
4935 #if 0
4936   printf("::: %02x, %d: %d != %d\n",
4937          byte_mask, conf_type & CONF_MASK_TOKEN,
4938          value, default_value);
4939 #endif
4940
4941   if (element != -1)
4942     num_bytes += putFile16BitBE(file, element);
4943
4944   num_bytes += putFile8Bit(file, conf_type);
4945   num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
4946                 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
4947                 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :0);
4948
4949   return num_bytes;
4950 }
4951
4952 static int SaveLevel_MicroChunk_ElementList(FILE *file,
4953                                             struct ElementFileConfig *entry)
4954 {
4955   int *element_array = (int *)(entry->value);
4956   int num_elements = *(int *)(entry->num_entities);
4957   int default_value = entry->default_value;
4958   int element = entry->element;
4959   int conf_type = entry->conf_type;
4960   int num_bytes = 0;
4961   boolean modified = FALSE;
4962   int i;
4963
4964   /* check if any settings have been modified before saving them */
4965   for (i = 0; i < num_elements; i++)
4966     if (element_array[i] != default_value)
4967       modified = TRUE;
4968
4969   if (!modified)                /* do not save unmodified default settings */
4970     return 0;
4971
4972   if (element != -1)
4973     num_bytes += putFile16BitBE(file, element);
4974
4975   num_bytes += putFile8Bit(file, conf_type);
4976   num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
4977
4978   for (i = 0; i < num_elements; i++)
4979     num_bytes += putFile16BitBE(file, element_array[i]);
4980
4981   return num_bytes;
4982 }
4983
4984 static int SaveLevel_MicroChunk_ContentList(FILE *file,
4985                                             struct ElementFileConfig *entry)
4986 {
4987   struct Content *content = (struct Content *)(entry->value);
4988   int num_contents = *(int *)(entry->num_entities);
4989   int default_value = entry->default_value;
4990   int element = entry->element;
4991   int conf_type = entry->conf_type;
4992   int num_bytes = 0;
4993   boolean modified = FALSE;
4994   int i, x, y;
4995
4996   /* check if any settings have been modified before saving them */
4997   for (i = 0; i < num_contents; i++)
4998     for (y = 0; y < 3; y++)
4999       for (x = 0; x < 3; x++)
5000         if (content[i].e[x][y] != default_value)
5001           modified = TRUE;
5002
5003   if (!modified)                /* do not save unmodified default settings */
5004     return 0;
5005
5006   if (element != -1)
5007     num_bytes += putFile16BitBE(file, element);
5008
5009   num_bytes += putFile8Bit(file, conf_type);
5010   num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
5011
5012   for (i = 0; i < num_contents; i++)
5013     for (y = 0; y < 3; y++)
5014       for (x = 0; x < 3; x++)
5015         num_bytes += putFile16BitBE(file, content[i].e[x][y]);
5016
5017   return num_bytes;
5018 }
5019
5020 #endif
5021
5022 static int SaveLevel_CONF(FILE *file, struct LevelInfo *level)
5023 {
5024   int chunk_size = 0;
5025   int i;
5026
5027   li = *level;          /* copy level data into temporary buffer */
5028
5029   for (i = 0; element_conf[i].data_type != -1; i++)
5030   {
5031 #if 1
5032     chunk_size += SaveLevel_MicroChunk(file, &element_conf[i]);
5033 #else
5034     struct ElementFileConfig *config = &element_conf[i];
5035     int data_type = config->data_type;
5036     int conf_type = config->conf_type;
5037     int byte_mask = conf_type & CONF_MASK_BYTES;
5038
5039     if (byte_mask != CONF_MASK_MULTI_BYTES)
5040       chunk_size += SaveLevel_MicroChunk_SingleValue(file, config);
5041     else if (data_type == TYPE_ELEMENT_LIST)
5042       chunk_size += SaveLevel_MicroChunk_ElementList(file, config);
5043     else if (data_type == TYPE_CONTENT_LIST)
5044       chunk_size += SaveLevel_MicroChunk_ContentList(file, config);
5045 #endif
5046   }
5047
5048   return chunk_size;
5049 }
5050
5051 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
5052 {
5053   struct ElementInfo *ei = &element_info[element];
5054   int chunk_size = 0;
5055   int i;
5056
5057   chunk_size += putFile16BitBE(file, element);
5058
5059   xx_ei = *ei;          /* copy element data into temporary buffer */
5060
5061   /* set default description string for this specific element */
5062   strcpy(xx_default_description, getDefaultElementDescription(ei));
5063
5064   /* set (fixed) number of content areas (may have been overwritten earlier) */
5065   xx_num_contents = 1;
5066
5067 #if 0
5068   printf("::: - element config\n");
5069 #endif
5070
5071   for (i = 0; custom_element_conf[i].data_type != -1; i++)
5072     chunk_size += SaveLevel_MicroChunk(file, &custom_element_conf[i]);
5073
5074 #if 0
5075   printf("::: - change pages\n");
5076 #endif
5077
5078   for (i = 0; i < ei->num_change_pages; i++)
5079   {
5080     struct ElementChangeInfo *change = &ei->change_page[i];
5081
5082     xx_current_change_page = i;
5083
5084     xx_change = *change;        /* copy change data into temporary buffer */
5085
5086 #if 0
5087     printf(":::   %d: xx_change.action_mode == %d\n",
5088            i, xx_change.action_mode);
5089     printf(":::   %d: xx_change.action_arg == %d\n",
5090            i, xx_change.action_arg);
5091 #endif
5092
5093     xx_event_bits_0_31 = 0;
5094     xx_event_bits_32_63 = 0;
5095
5096     for (i = 0; i < NUM_CHANGE_EVENTS; i++)
5097     {
5098       if (change->has_event[i])
5099       {
5100         if (i < 32)
5101           xx_event_bits_0_31 |= (1 << i);
5102         else
5103           xx_event_bits_32_63 |= (1 << (i - 32));
5104       }
5105     }
5106
5107     for (i = 0; custom_element_change_conf[i].data_type != -1; i++)
5108       chunk_size += SaveLevel_MicroChunk(file, &custom_element_change_conf[i]);
5109   }
5110
5111   return chunk_size;
5112 }
5113
5114 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
5115 {
5116   struct ElementInfo *ei = &element_info[element];
5117   struct ElementGroupInfo *group = ei->group;
5118   int chunk_size = 0;
5119   int i;
5120
5121   chunk_size += putFile16BitBE(file, element);
5122
5123   xx_ei = *ei;          /* copy element data into temporary buffer */
5124   xx_group = *group;    /* copy group data into temporary buffer */
5125
5126   /* set default description string for this specific element */
5127   strcpy(xx_default_description, getDefaultElementDescription(ei));
5128
5129   for (i = 0; group_element_conf[i].data_type != -1; i++)
5130     chunk_size += SaveLevel_MicroChunk(file, &group_element_conf[i]);
5131
5132   return chunk_size;
5133 }
5134
5135 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
5136 {
5137   int body_chunk_size, conf_chunk_size;
5138   int i, x, y;
5139   FILE *file;
5140
5141   if (!(file = fopen(filename, MODE_WRITE)))
5142   {
5143     Error(ERR_WARN, "cannot save level file '%s'", filename);
5144     return;
5145   }
5146
5147   level->file_version = FILE_VERSION_ACTUAL;
5148   level->game_version = GAME_VERSION_ACTUAL;
5149
5150   /* check level field for 16-bit elements */
5151   level->encoding_16bit_field = FALSE;
5152   for (y = 0; y < level->fieldy; y++) 
5153     for (x = 0; x < level->fieldx; x++) 
5154       if (level->field[x][y] > 255)
5155         level->encoding_16bit_field = TRUE;
5156
5157   /* check yamyam content for 16-bit elements */
5158   level->encoding_16bit_yamyam = FALSE;
5159   for (i = 0; i < level->num_yamyam_contents; i++)
5160     for (y = 0; y < 3; y++)
5161       for (x = 0; x < 3; x++)
5162         if (level->yamyam_content[i].e[x][y] > 255)
5163           level->encoding_16bit_yamyam = TRUE;
5164
5165   /* check amoeba content for 16-bit elements */
5166   level->encoding_16bit_amoeba = FALSE;
5167   if (level->amoeba_content > 255)
5168     level->encoding_16bit_amoeba = TRUE;
5169
5170   /* calculate size of "BODY" chunk */
5171   body_chunk_size =
5172     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
5173
5174   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
5175   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
5176
5177   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
5178   SaveLevel_VERS(file, level);
5179
5180   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
5181   SaveLevel_HEAD(file, level);
5182
5183   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
5184   SaveLevel_AUTH(file, level);
5185
5186   putFileChunkBE(file, "BODY", body_chunk_size);
5187   SaveLevel_BODY(file, level);
5188
5189   if (level->encoding_16bit_yamyam ||
5190       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
5191   {
5192     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
5193     SaveLevel_CNT2(file, level, EL_YAMYAM);
5194   }
5195
5196   if (level->encoding_16bit_amoeba)
5197   {
5198     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
5199     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
5200   }
5201
5202   /* check for envelope content */
5203   for (i = 0; i < 4; i++)
5204   {
5205     if (strlen(level->envelope_text[i]) > 0)
5206     {
5207       int envelope_len = strlen(level->envelope_text[i]) + 1;
5208
5209       putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
5210       SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
5211     }
5212   }
5213
5214   /* if not using template level, check for non-default custom/group elements */
5215   if (!level->use_custom_template)
5216   {
5217 #if 1
5218     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5219     {
5220       int element = EL_CUSTOM_START + i;
5221
5222       if (element_info[element].modified_settings)
5223       {
5224         int num_change_pages = element_info[element].num_change_pages;
5225
5226         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
5227         SaveLevel_CUS4(file, level, element);
5228       }
5229     }
5230 #endif
5231
5232 #if 1
5233     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
5234     {
5235       int element = EL_GROUP_START + i;
5236
5237       if (element_info[element].modified_settings)
5238       {
5239         putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
5240         SaveLevel_GRP1(file, level, element);
5241       }
5242     }
5243 #endif
5244   }
5245
5246   conf_chunk_size = SaveLevel_CONF(NULL, level);
5247
5248   /* check if non-default element settings need to be saved */
5249   if (conf_chunk_size > 0)
5250   {
5251     putFileChunkBE(file, "CONF", conf_chunk_size);
5252     SaveLevel_CONF(file, level);
5253   }
5254
5255   /* if not using template level, check for non-default custom/group elements */
5256   if (!level->use_custom_template)
5257   {
5258     /* (element number, number of change pages, change page number) */
5259     int cusx_chunk_size_no_changes = (2) + (1 + 1) + (1 + 1);
5260     /* (element number only) */
5261     int grpx_chunk_size_no_changes = (2);
5262
5263 #if 1
5264     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
5265     {
5266       int element = EL_CUSTOM_START + i;
5267       int cusx_chunk_size = SaveLevel_CUSX(NULL, level, element);
5268
5269       /* check if non-default element settings need to be saved */
5270       if (cusx_chunk_size > cusx_chunk_size_no_changes)
5271       {
5272 #if 1
5273         printf("::: SAVING CE %d\n", i + 1);
5274 #endif
5275
5276         putFileChunkBE(file, "CUSX", cusx_chunk_size);
5277         SaveLevel_CUSX(file, level, element);
5278       }
5279
5280 #if 0
5281       if (i == 1)
5282         break;
5283 #endif
5284     }
5285 #endif
5286
5287 #if 1
5288     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
5289     {
5290       int element = EL_GROUP_START + i;
5291       int grpx_chunk_size = SaveLevel_GRPX(NULL, level, element);
5292
5293       /* check if non-default element settings need to be saved */
5294       if (grpx_chunk_size > grpx_chunk_size_no_changes)
5295       {
5296 #if 1
5297         printf("::: SAVING GE %d\n", i + 1);
5298 #endif
5299
5300         putFileChunkBE(file, "GRPX", grpx_chunk_size);
5301         SaveLevel_GRPX(file, level, element);
5302       }
5303
5304 #if 0
5305       if (i == 1)
5306         break;
5307 #endif
5308     }
5309 #endif
5310   }
5311
5312   fclose(file);
5313
5314   SetFilePermissions(filename, PERMS_PRIVATE);
5315 }
5316
5317 void SaveLevel(int nr)
5318 {
5319   char *filename = getDefaultLevelFilename(nr);
5320
5321   SaveLevelFromFilename(&level, filename);
5322 }
5323
5324 void SaveLevelTemplate()
5325 {
5326   char *filename = getDefaultLevelFilename(-1);
5327
5328   SaveLevelFromFilename(&level, filename);
5329 }
5330
5331 void DumpLevel(struct LevelInfo *level)
5332 {
5333   if (level->no_valid_file)
5334   {
5335     Error(ERR_WARN, "cannot dump -- no valid level file found");
5336
5337     return;
5338   }
5339
5340   printf_line("-", 79);
5341   printf("Level xxx (file version %08d, game version %08d)\n",
5342          level->file_version, level->game_version);
5343   printf_line("-", 79);
5344
5345   printf("Level author: '%s'\n", level->author);
5346   printf("Level title:  '%s'\n", level->name);
5347   printf("\n");
5348   printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
5349   printf("\n");
5350   printf("Level time:  %d seconds\n", level->time);
5351   printf("Gems needed: %d\n", level->gems_needed);
5352   printf("\n");
5353   printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
5354   printf("Time for wheel:      %d seconds\n", level->time_wheel);
5355   printf("Time for light:      %d seconds\n", level->time_light);
5356   printf("Time for timegate:   %d seconds\n", level->time_timegate);
5357   printf("\n");
5358   printf("Amoeba speed: %d\n", level->amoeba_speed);
5359   printf("\n");
5360   printf("Initial gravity:             %s\n", (level->initial_gravity ? "yes" : "no"));
5361   printf("Initial player stepsize:     %d\n", level->initial_player_stepsize);
5362   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
5363   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
5364   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
5365   printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
5366   printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
5367
5368   printf_line("-", 79);
5369 }
5370
5371
5372 /* ========================================================================= */
5373 /* tape file functions                                                       */
5374 /* ========================================================================= */
5375
5376 static void setTapeInfoToDefaults()
5377 {
5378   int i;
5379
5380   /* always start with reliable default values (empty tape) */
5381   TapeErase();
5382
5383   /* default values (also for pre-1.2 tapes) with only the first player */
5384   tape.player_participates[0] = TRUE;
5385   for (i = 1; i < MAX_PLAYERS; i++)
5386     tape.player_participates[i] = FALSE;
5387
5388   /* at least one (default: the first) player participates in every tape */
5389   tape.num_participating_players = 1;
5390
5391   tape.level_nr = level_nr;
5392   tape.counter = 0;
5393   tape.changed = FALSE;
5394
5395   tape.recording = FALSE;
5396   tape.playing = FALSE;
5397   tape.pausing = FALSE;
5398
5399   tape.no_valid_file = FALSE;
5400 }
5401
5402 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
5403 {
5404   tape->file_version = getFileVersion(file);
5405   tape->game_version = getFileVersion(file);
5406
5407   return chunk_size;
5408 }
5409
5410 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
5411 {
5412   int i;
5413
5414   tape->random_seed = getFile32BitBE(file);
5415   tape->date        = getFile32BitBE(file);
5416   tape->length      = getFile32BitBE(file);
5417
5418   /* read header fields that are new since version 1.2 */
5419   if (tape->file_version >= FILE_VERSION_1_2)
5420   {
5421     byte store_participating_players = getFile8Bit(file);
5422     int engine_version;
5423
5424     /* since version 1.2, tapes store which players participate in the tape */
5425     tape->num_participating_players = 0;
5426     for (i = 0; i < MAX_PLAYERS; i++)
5427     {
5428       tape->player_participates[i] = FALSE;
5429
5430       if (store_participating_players & (1 << i))
5431       {
5432         tape->player_participates[i] = TRUE;
5433         tape->num_participating_players++;
5434       }
5435     }
5436
5437     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
5438
5439     engine_version = getFileVersion(file);
5440     if (engine_version > 0)
5441       tape->engine_version = engine_version;
5442     else
5443       tape->engine_version = tape->game_version;
5444   }
5445
5446   return chunk_size;
5447 }
5448
5449 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
5450 {
5451   int level_identifier_size;
5452   int i;
5453
5454   level_identifier_size = getFile16BitBE(file);
5455
5456   tape->level_identifier =
5457     checked_realloc(tape->level_identifier, level_identifier_size);
5458
5459   for (i = 0; i < level_identifier_size; i++)
5460     tape->level_identifier[i] = getFile8Bit(file);
5461
5462   tape->level_nr = getFile16BitBE(file);
5463
5464   chunk_size = 2 + level_identifier_size + 2;
5465
5466   return chunk_size;
5467 }
5468
5469 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
5470 {
5471   int i, j;
5472   int chunk_size_expected =
5473     (tape->num_participating_players + 1) * tape->length;
5474
5475   if (chunk_size_expected != chunk_size)
5476   {
5477     ReadUnusedBytesFromFile(file, chunk_size);
5478     return chunk_size_expected;
5479   }
5480
5481   for (i = 0; i < tape->length; i++)
5482   {
5483     if (i >= MAX_TAPE_LEN)
5484       break;
5485
5486     for (j = 0; j < MAX_PLAYERS; j++)
5487     {
5488       tape->pos[i].action[j] = MV_NONE;
5489
5490       if (tape->player_participates[j])
5491         tape->pos[i].action[j] = getFile8Bit(file);
5492     }
5493
5494     tape->pos[i].delay = getFile8Bit(file);
5495
5496     if (tape->file_version == FILE_VERSION_1_0)
5497     {
5498       /* eliminate possible diagonal moves in old tapes */
5499       /* this is only for backward compatibility */
5500
5501       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
5502       byte action = tape->pos[i].action[0];
5503       int k, num_moves = 0;
5504
5505       for (k = 0; k<4; k++)
5506       {
5507         if (action & joy_dir[k])
5508         {
5509           tape->pos[i + num_moves].action[0] = joy_dir[k];
5510           if (num_moves > 0)
5511             tape->pos[i + num_moves].delay = 0;
5512           num_moves++;
5513         }
5514       }
5515
5516       if (num_moves > 1)
5517       {
5518         num_moves--;
5519         i += num_moves;
5520         tape->length += num_moves;
5521       }
5522     }
5523     else if (tape->file_version < FILE_VERSION_2_0)
5524     {
5525       /* convert pre-2.0 tapes to new tape format */
5526
5527       if (tape->pos[i].delay > 1)
5528       {
5529         /* action part */
5530         tape->pos[i + 1] = tape->pos[i];
5531         tape->pos[i + 1].delay = 1;
5532
5533         /* delay part */
5534         for (j = 0; j < MAX_PLAYERS; j++)
5535           tape->pos[i].action[j] = MV_NONE;
5536         tape->pos[i].delay--;
5537
5538         i++;
5539         tape->length++;
5540       }
5541     }
5542
5543     if (feof(file))
5544       break;
5545   }
5546
5547   if (i != tape->length)
5548     chunk_size = (tape->num_participating_players + 1) * i;
5549
5550   return chunk_size;
5551 }
5552
5553 void LoadTapeFromFilename(char *filename)
5554 {
5555   char cookie[MAX_LINE_LEN];
5556   char chunk_name[CHUNK_ID_LEN + 1];
5557   FILE *file;
5558   int chunk_size;
5559
5560   /* always start with reliable default values */
5561   setTapeInfoToDefaults();
5562
5563   if (!(file = fopen(filename, MODE_READ)))
5564   {
5565     tape.no_valid_file = TRUE;
5566
5567     return;
5568   }
5569
5570   getFileChunkBE(file, chunk_name, NULL);
5571   if (strEqual(chunk_name, "RND1"))
5572   {
5573     getFile32BitBE(file);               /* not used */
5574
5575     getFileChunkBE(file, chunk_name, NULL);
5576     if (!strEqual(chunk_name, "TAPE"))
5577     {
5578       tape.no_valid_file = TRUE;
5579
5580       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
5581       fclose(file);
5582       return;
5583     }
5584   }
5585   else  /* check for pre-2.0 file format with cookie string */
5586   {
5587     strcpy(cookie, chunk_name);
5588     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
5589     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
5590       cookie[strlen(cookie) - 1] = '\0';
5591
5592     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
5593     {
5594       tape.no_valid_file = TRUE;
5595
5596       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
5597       fclose(file);
5598       return;
5599     }
5600
5601     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
5602     {
5603       tape.no_valid_file = TRUE;
5604
5605       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
5606       fclose(file);
5607       return;
5608     }
5609
5610     /* pre-2.0 tape files have no game version, so use file version here */
5611     tape.game_version = tape.file_version;
5612   }
5613
5614   if (tape.file_version < FILE_VERSION_1_2)
5615   {
5616     /* tape files from versions before 1.2.0 without chunk structure */
5617     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
5618     LoadTape_BODY(file, 2 * tape.length,  &tape);
5619   }
5620   else
5621   {
5622     static struct
5623     {
5624       char *name;
5625       int size;
5626       int (*loader)(FILE *, int, struct TapeInfo *);
5627     }
5628     chunk_info[] =
5629     {
5630       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
5631       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
5632       { "INFO", -1,                     LoadTape_INFO },
5633       { "BODY", -1,                     LoadTape_BODY },
5634       {  NULL,  0,                      NULL }
5635     };
5636
5637     while (getFileChunkBE(file, chunk_name, &chunk_size))
5638     {
5639       int i = 0;
5640
5641       while (chunk_info[i].name != NULL &&
5642              !strEqual(chunk_name, chunk_info[i].name))
5643         i++;
5644
5645       if (chunk_info[i].name == NULL)
5646       {
5647         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
5648               chunk_name, filename);
5649         ReadUnusedBytesFromFile(file, chunk_size);
5650       }
5651       else if (chunk_info[i].size != -1 &&
5652                chunk_info[i].size != chunk_size)
5653       {
5654         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
5655               chunk_size, chunk_name, filename);
5656         ReadUnusedBytesFromFile(file, chunk_size);
5657       }
5658       else
5659       {
5660         /* call function to load this tape chunk */
5661         int chunk_size_expected =
5662           (chunk_info[i].loader)(file, chunk_size, &tape);
5663
5664         /* the size of some chunks cannot be checked before reading other
5665            chunks first (like "HEAD" and "BODY") that contain some header
5666            information, so check them here */
5667         if (chunk_size_expected != chunk_size)
5668         {
5669           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
5670                 chunk_size, chunk_name, filename);
5671         }
5672       }
5673     }
5674   }
5675
5676   fclose(file);
5677
5678   tape.length_seconds = GetTapeLength();
5679
5680 #if 0
5681   printf("::: tape game version: %d\n", tape.game_version);
5682   printf("::: tape engine version: %d\n", tape.engine_version);
5683 #endif
5684 }
5685
5686 void LoadTape(int nr)
5687 {
5688   char *filename = getTapeFilename(nr);
5689
5690   LoadTapeFromFilename(filename);
5691 }
5692
5693 void LoadSolutionTape(int nr)
5694 {
5695   char *filename = getSolutionTapeFilename(nr);
5696
5697   LoadTapeFromFilename(filename);
5698 }
5699
5700 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
5701 {
5702   putFileVersion(file, tape->file_version);
5703   putFileVersion(file, tape->game_version);
5704 }
5705
5706 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
5707 {
5708   int i;
5709   byte store_participating_players = 0;
5710
5711   /* set bits for participating players for compact storage */
5712   for (i = 0; i < MAX_PLAYERS; i++)
5713     if (tape->player_participates[i])
5714       store_participating_players |= (1 << i);
5715
5716   putFile32BitBE(file, tape->random_seed);
5717   putFile32BitBE(file, tape->date);
5718   putFile32BitBE(file, tape->length);
5719
5720   putFile8Bit(file, store_participating_players);
5721
5722   /* unused bytes not at the end here for 4-byte alignment of engine_version */
5723   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
5724
5725   putFileVersion(file, tape->engine_version);
5726 }
5727
5728 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
5729 {
5730   int level_identifier_size = strlen(tape->level_identifier) + 1;
5731   int i;
5732
5733   putFile16BitBE(file, level_identifier_size);
5734
5735   for (i = 0; i < level_identifier_size; i++)
5736     putFile8Bit(file, tape->level_identifier[i]);
5737
5738   putFile16BitBE(file, tape->level_nr);
5739 }
5740
5741 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
5742 {
5743   int i, j;
5744
5745   for (i = 0; i < tape->length; i++)
5746   {
5747     for (j = 0; j < MAX_PLAYERS; j++)
5748       if (tape->player_participates[j])
5749         putFile8Bit(file, tape->pos[i].action[j]);
5750
5751     putFile8Bit(file, tape->pos[i].delay);
5752   }
5753 }
5754
5755 void SaveTape(int nr)
5756 {
5757   char *filename = getTapeFilename(nr);
5758   FILE *file;
5759   boolean new_tape = TRUE;
5760   int num_participating_players = 0;
5761   int info_chunk_size;
5762   int body_chunk_size;
5763   int i;
5764
5765   InitTapeDirectory(leveldir_current->subdir);
5766
5767   /* if a tape still exists, ask to overwrite it */
5768   if (fileExists(filename))
5769   {
5770     new_tape = FALSE;
5771     if (!Request("Replace old tape ?", REQ_ASK))
5772       return;
5773   }
5774
5775   if (!(file = fopen(filename, MODE_WRITE)))
5776   {
5777     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
5778     return;
5779   }
5780
5781   tape.file_version = FILE_VERSION_ACTUAL;
5782   tape.game_version = GAME_VERSION_ACTUAL;
5783
5784   /* count number of participating players  */
5785   for (i = 0; i < MAX_PLAYERS; i++)
5786     if (tape.player_participates[i])
5787       num_participating_players++;
5788
5789   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
5790   body_chunk_size = (num_participating_players + 1) * tape.length;
5791
5792   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
5793   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
5794
5795   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
5796   SaveTape_VERS(file, &tape);
5797
5798   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
5799   SaveTape_HEAD(file, &tape);
5800
5801   putFileChunkBE(file, "INFO", info_chunk_size);
5802   SaveTape_INFO(file, &tape);
5803
5804   putFileChunkBE(file, "BODY", body_chunk_size);
5805   SaveTape_BODY(file, &tape);
5806
5807   fclose(file);
5808
5809   SetFilePermissions(filename, PERMS_PRIVATE);
5810
5811   tape.changed = FALSE;
5812
5813   if (new_tape)
5814     Request("Tape saved !", REQ_CONFIRM);
5815 }
5816
5817 void DumpTape(struct TapeInfo *tape)
5818 {
5819   int tape_frame_counter;
5820   int i, j;
5821
5822   if (tape->no_valid_file)
5823   {
5824     Error(ERR_WARN, "cannot dump -- no valid tape file found");
5825
5826     return;
5827   }
5828
5829   printf_line("-", 79);
5830   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
5831          tape->level_nr, tape->file_version, tape->game_version);
5832   printf("                  (effective engine version %08d)\n",
5833          tape->engine_version);
5834   printf("Level series identifier: '%s'\n", tape->level_identifier);
5835   printf_line("-", 79);
5836
5837   tape_frame_counter = 0;
5838
5839   for (i = 0; i < tape->length; i++)
5840   {
5841     if (i >= MAX_TAPE_LEN)
5842       break;
5843
5844     printf("%04d: ", i);
5845
5846     for (j = 0; j < MAX_PLAYERS; j++)
5847     {
5848       if (tape->player_participates[j])
5849       {
5850         int action = tape->pos[i].action[j];
5851
5852         printf("%d:%02x ", j, action);
5853         printf("[%c%c%c%c|%c%c] - ",
5854                (action & JOY_LEFT ? '<' : ' '),
5855                (action & JOY_RIGHT ? '>' : ' '),
5856                (action & JOY_UP ? '^' : ' '),
5857                (action & JOY_DOWN ? 'v' : ' '),
5858                (action & JOY_BUTTON_1 ? '1' : ' '),
5859                (action & JOY_BUTTON_2 ? '2' : ' '));
5860       }
5861     }
5862
5863     printf("(%03d) ", tape->pos[i].delay);
5864     printf("[%05d]\n", tape_frame_counter);
5865
5866     tape_frame_counter += tape->pos[i].delay;
5867   }
5868
5869   printf_line("-", 79);
5870 }
5871
5872
5873 /* ========================================================================= */
5874 /* score file functions                                                      */
5875 /* ========================================================================= */
5876
5877 void LoadScore(int nr)
5878 {
5879   int i;
5880   char *filename = getScoreFilename(nr);
5881   char cookie[MAX_LINE_LEN];
5882   char line[MAX_LINE_LEN];
5883   char *line_ptr;
5884   FILE *file;
5885
5886   /* always start with reliable default values */
5887   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5888   {
5889     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
5890     highscore[i].Score = 0;
5891   }
5892
5893   if (!(file = fopen(filename, MODE_READ)))
5894     return;
5895
5896   /* check file identifier */
5897   fgets(cookie, MAX_LINE_LEN, file);
5898   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
5899     cookie[strlen(cookie) - 1] = '\0';
5900
5901   if (!checkCookieString(cookie, SCORE_COOKIE))
5902   {
5903     Error(ERR_WARN, "unknown format of score file '%s'", filename);
5904     fclose(file);
5905     return;
5906   }
5907
5908   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5909   {
5910     fscanf(file, "%d", &highscore[i].Score);
5911     fgets(line, MAX_LINE_LEN, file);
5912
5913     if (line[strlen(line) - 1] == '\n')
5914       line[strlen(line) - 1] = '\0';
5915
5916     for (line_ptr = line; *line_ptr; line_ptr++)
5917     {
5918       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
5919       {
5920         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
5921         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
5922         break;
5923       }
5924     }
5925   }
5926
5927   fclose(file);
5928 }
5929
5930 void SaveScore(int nr)
5931 {
5932   int i;
5933   char *filename = getScoreFilename(nr);
5934   FILE *file;
5935
5936   InitScoreDirectory(leveldir_current->subdir);
5937
5938   if (!(file = fopen(filename, MODE_WRITE)))
5939   {
5940     Error(ERR_WARN, "cannot save score for level %d", nr);
5941     return;
5942   }
5943
5944   fprintf(file, "%s\n\n", SCORE_COOKIE);
5945
5946   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5947     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
5948
5949   fclose(file);
5950
5951   SetFilePermissions(filename, PERMS_PUBLIC);
5952 }
5953
5954
5955 /* ========================================================================= */
5956 /* setup file functions                                                      */
5957 /* ========================================================================= */
5958
5959 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
5960
5961 /* global setup */
5962 #define SETUP_TOKEN_PLAYER_NAME                 0
5963 #define SETUP_TOKEN_SOUND                       1
5964 #define SETUP_TOKEN_SOUND_LOOPS                 2
5965 #define SETUP_TOKEN_SOUND_MUSIC                 3
5966 #define SETUP_TOKEN_SOUND_SIMPLE                4
5967 #define SETUP_TOKEN_TOONS                       5
5968 #define SETUP_TOKEN_SCROLL_DELAY                6
5969 #define SETUP_TOKEN_SOFT_SCROLLING              7
5970 #define SETUP_TOKEN_FADING                      8
5971 #define SETUP_TOKEN_AUTORECORD                  9
5972 #define SETUP_TOKEN_SHOW_TITLESCREEN            10
5973 #define SETUP_TOKEN_QUICK_DOORS                 11
5974 #define SETUP_TOKEN_TEAM_MODE                   12
5975 #define SETUP_TOKEN_HANDICAP                    13
5976 #define SETUP_TOKEN_SKIP_LEVELS                 14
5977 #define SETUP_TOKEN_TIME_LIMIT                  15
5978 #define SETUP_TOKEN_FULLSCREEN                  16
5979 #define SETUP_TOKEN_ASK_ON_ESCAPE               17
5980 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        18
5981 #define SETUP_TOKEN_QUICK_SWITCH                19
5982 #define SETUP_TOKEN_INPUT_ON_FOCUS              20
5983 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         21
5984 #define SETUP_TOKEN_GRAPHICS_SET                22
5985 #define SETUP_TOKEN_SOUNDS_SET                  23
5986 #define SETUP_TOKEN_MUSIC_SET                   24
5987 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     25
5988 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       26
5989 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        27
5990
5991 #define NUM_GLOBAL_SETUP_TOKENS                 28
5992
5993 /* editor setup */
5994 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
5995 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
5996 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
5997 #define SETUP_TOKEN_EDITOR_EL_MORE              3
5998 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           4
5999 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          5
6000 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     6
6001 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    7
6002 #define SETUP_TOKEN_EDITOR_EL_CHARS             8
6003 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            9
6004 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
6005 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      11
6006 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           12
6007 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   13
6008
6009 #define NUM_EDITOR_SETUP_TOKENS                 14
6010
6011 /* editor cascade setup */
6012 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
6013 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
6014 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
6015 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
6016 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
6017 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
6018 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
6019 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
6020 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
6021 #define SETUP_TOKEN_EDITOR_CASCADE_CE           9
6022 #define SETUP_TOKEN_EDITOR_CASCADE_GE           10
6023 #define SETUP_TOKEN_EDITOR_CASCADE_USER         11
6024 #define SETUP_TOKEN_EDITOR_CASCADE_GENERIC      12
6025 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      13
6026
6027 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         14
6028
6029 /* shortcut setup */
6030 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
6031 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
6032 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
6033 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
6034 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
6035 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
6036 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
6037 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
6038
6039 #define NUM_SHORTCUT_SETUP_TOKENS               8
6040
6041 /* player setup */
6042 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
6043 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
6044 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
6045 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
6046 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
6047 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
6048 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
6049 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
6050 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
6051 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
6052 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
6053 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
6054 #define SETUP_TOKEN_PLAYER_KEY_UP               12
6055 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
6056 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
6057 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
6058
6059 #define NUM_PLAYER_SETUP_TOKENS                 16
6060
6061 /* system setup */
6062 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
6063 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
6064
6065 #define NUM_SYSTEM_SETUP_TOKENS                 2
6066
6067 /* options setup */
6068 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
6069
6070 #define NUM_OPTIONS_SETUP_TOKENS                1
6071
6072
6073 static struct SetupInfo si;
6074 static struct SetupEditorInfo sei;
6075 static struct SetupEditorCascadeInfo seci;
6076 static struct SetupShortcutInfo ssi;
6077 static struct SetupInputInfo sii;
6078 static struct SetupSystemInfo syi;
6079 static struct OptionInfo soi;
6080
6081 static struct TokenInfo global_setup_tokens[] =
6082 {
6083   { TYPE_STRING, &si.player_name,       "player_name"                   },
6084   { TYPE_SWITCH, &si.sound,             "sound"                         },
6085   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
6086   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
6087   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
6088   { TYPE_SWITCH, &si.toons,             "toons"                         },
6089   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
6090   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
6091   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
6092   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
6093   { TYPE_SWITCH, &si.show_titlescreen,  "show_titlescreen"              },
6094   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
6095   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
6096   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
6097   { TYPE_SWITCH, &si.skip_levels,       "skip_levels"                   },
6098   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
6099   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
6100   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
6101   { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor"       },
6102   { TYPE_SWITCH, &si.quick_switch,      "quick_player_switch"           },
6103   { TYPE_SWITCH, &si.input_on_focus,    "input_on_focus"                },
6104   { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics"         },
6105   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
6106   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
6107   { TYPE_STRING, &si.music_set,         "music_set"                     },
6108   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
6109   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
6110   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
6111 };
6112
6113 static struct TokenInfo editor_setup_tokens[] =
6114 {
6115   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
6116   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
6117   { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
6118   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
6119   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
6120   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
6121   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
6122   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
6123   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
6124   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
6125   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
6126   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
6127   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
6128   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
6129 };
6130
6131 static struct TokenInfo editor_cascade_setup_tokens[] =
6132 {
6133   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
6134   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
6135   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
6136   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
6137   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
6138   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
6139   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
6140   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
6141   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
6142   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
6143   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
6144   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
6145   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
6146 };
6147
6148 static struct TokenInfo shortcut_setup_tokens[] =
6149 {
6150   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
6151   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
6152   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
6153   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
6154   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
6155   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
6156   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
6157   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
6158 };
6159
6160 static struct TokenInfo player_setup_tokens[] =
6161 {
6162   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
6163   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
6164   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
6165   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
6166   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
6167   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
6168   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
6169   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
6170   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
6171   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
6172   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
6173   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
6174   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
6175   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
6176   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
6177   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
6178 };
6179
6180 static struct TokenInfo system_setup_tokens[] =
6181 {
6182   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
6183   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
6184 };
6185
6186 static struct TokenInfo options_setup_tokens[] =
6187 {
6188   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
6189 };
6190
6191 static char *get_corrected_login_name(char *login_name)
6192 {
6193   /* needed because player name must be a fixed length string */
6194   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
6195
6196   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
6197   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
6198
6199   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
6200     if (strchr(login_name_new, ' '))
6201       *strchr(login_name_new, ' ') = '\0';
6202
6203   return login_name_new;
6204 }
6205
6206 static void setSetupInfoToDefaults(struct SetupInfo *si)
6207 {
6208   int i;
6209
6210   si->player_name = get_corrected_login_name(getLoginName());
6211
6212   si->sound = TRUE;
6213   si->sound_loops = TRUE;
6214   si->sound_music = TRUE;
6215   si->sound_simple = TRUE;
6216   si->toons = TRUE;
6217   si->double_buffering = TRUE;
6218   si->direct_draw = !si->double_buffering;
6219   si->scroll_delay = TRUE;
6220   si->soft_scrolling = TRUE;
6221   si->fading = FALSE;
6222   si->autorecord = TRUE;
6223   si->show_titlescreen = TRUE;
6224   si->quick_doors = FALSE;
6225   si->team_mode = FALSE;
6226   si->handicap = TRUE;
6227   si->skip_levels = TRUE;
6228   si->time_limit = TRUE;
6229   si->fullscreen = FALSE;
6230   si->ask_on_escape = TRUE;
6231   si->ask_on_escape_editor = TRUE;
6232   si->quick_switch = FALSE;
6233   si->input_on_focus = FALSE;
6234   si->prefer_aga_graphics = TRUE;
6235
6236   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
6237   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
6238   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
6239   si->override_level_graphics = FALSE;
6240   si->override_level_sounds = FALSE;
6241   si->override_level_music = FALSE;
6242
6243   si->editor.el_boulderdash       = TRUE;
6244   si->editor.el_emerald_mine      = TRUE;
6245   si->editor.el_emerald_mine_club = TRUE;
6246   si->editor.el_more              = TRUE;
6247   si->editor.el_sokoban           = TRUE;
6248   si->editor.el_supaplex          = TRUE;
6249   si->editor.el_diamond_caves     = TRUE;
6250   si->editor.el_dx_boulderdash    = TRUE;
6251   si->editor.el_chars             = TRUE;
6252   si->editor.el_custom            = TRUE;
6253
6254   si->editor.el_headlines = TRUE;
6255   si->editor.el_user_defined = FALSE;
6256   si->editor.el_dynamic = TRUE;
6257
6258   si->editor.show_element_token = FALSE;
6259
6260   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
6261   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
6262   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
6263
6264   si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
6265   si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
6266   si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
6267   si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
6268   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
6269
6270   for (i = 0; i < MAX_PLAYERS; i++)
6271   {
6272     si->input[i].use_joystick = FALSE;
6273     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
6274     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
6275     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
6276     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
6277     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
6278     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
6279     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
6280     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
6281     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
6282     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
6283     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
6284     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
6285     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
6286     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
6287     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
6288   }
6289
6290   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
6291   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
6292
6293   si->options.verbose = FALSE;
6294 }
6295
6296 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
6297 {
6298   si->editor_cascade.el_bd      = TRUE;
6299   si->editor_cascade.el_em      = TRUE;
6300   si->editor_cascade.el_emc     = TRUE;
6301   si->editor_cascade.el_rnd     = TRUE;
6302   si->editor_cascade.el_sb      = TRUE;
6303   si->editor_cascade.el_sp      = TRUE;
6304   si->editor_cascade.el_dc      = TRUE;
6305   si->editor_cascade.el_dx      = TRUE;
6306
6307   si->editor_cascade.el_chars   = FALSE;
6308   si->editor_cascade.el_ce      = FALSE;
6309   si->editor_cascade.el_ge      = FALSE;
6310   si->editor_cascade.el_user    = FALSE;
6311   si->editor_cascade.el_dynamic = FALSE;
6312 }
6313
6314 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
6315 {
6316   int i, pnr;
6317
6318   if (!setup_file_hash)
6319     return;
6320
6321   /* global setup */
6322   si = setup;
6323   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
6324     setSetupInfo(global_setup_tokens, i,
6325                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
6326   setup = si;
6327
6328   /* editor setup */
6329   sei = setup.editor;
6330   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
6331     setSetupInfo(editor_setup_tokens, i,
6332                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
6333   setup.editor = sei;
6334
6335   /* shortcut setup */
6336   ssi = setup.shortcut;
6337   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
6338     setSetupInfo(shortcut_setup_tokens, i,
6339                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
6340   setup.shortcut = ssi;
6341
6342   /* player setup */
6343   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
6344   {
6345     char prefix[30];
6346
6347     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
6348
6349     sii = setup.input[pnr];
6350     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
6351     {
6352       char full_token[100];
6353
6354       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
6355       setSetupInfo(player_setup_tokens, i,
6356                    getHashEntry(setup_file_hash, full_token));
6357     }
6358     setup.input[pnr] = sii;
6359   }
6360
6361   /* system setup */
6362   syi = setup.system;
6363   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
6364     setSetupInfo(system_setup_tokens, i,
6365                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
6366   setup.system = syi;
6367
6368   /* options setup */
6369   soi = setup.options;
6370   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
6371     setSetupInfo(options_setup_tokens, i,
6372                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
6373   setup.options = soi;
6374 }
6375
6376 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
6377 {
6378   int i;
6379
6380   if (!setup_file_hash)
6381     return;
6382
6383   /* editor cascade setup */
6384   seci = setup.editor_cascade;
6385   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
6386     setSetupInfo(editor_cascade_setup_tokens, i,
6387                  getHashEntry(setup_file_hash,
6388                               editor_cascade_setup_tokens[i].text));
6389   setup.editor_cascade = seci;
6390 }
6391
6392 void LoadSetup()
6393 {
6394   char *filename = getSetupFilename();
6395   SetupFileHash *setup_file_hash = NULL;
6396
6397   /* always start with reliable default values */
6398   setSetupInfoToDefaults(&setup);
6399
6400   setup_file_hash = loadSetupFileHash(filename);
6401
6402   if (setup_file_hash)
6403   {
6404     char *player_name_new;
6405
6406     checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
6407     decodeSetupFileHash(setup_file_hash);
6408
6409     setup.direct_draw = !setup.double_buffering;
6410
6411     freeSetupFileHash(setup_file_hash);
6412
6413     /* needed to work around problems with fixed length strings */
6414     player_name_new = get_corrected_login_name(setup.player_name);
6415     free(setup.player_name);
6416     setup.player_name = player_name_new;
6417   }
6418   else
6419     Error(ERR_WARN, "using default setup values");
6420 }
6421
6422 void LoadSetup_EditorCascade()
6423 {
6424   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
6425   SetupFileHash *setup_file_hash = NULL;
6426
6427   /* always start with reliable default values */
6428   setSetupInfoToDefaults_EditorCascade(&setup);
6429
6430   setup_file_hash = loadSetupFileHash(filename);
6431
6432   if (setup_file_hash)
6433   {
6434     checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
6435     decodeSetupFileHash_EditorCascade(setup_file_hash);
6436
6437     freeSetupFileHash(setup_file_hash);
6438   }
6439
6440   free(filename);
6441 }
6442
6443 void SaveSetup()
6444 {
6445   char *filename = getSetupFilename();
6446   FILE *file;
6447   int i, pnr;
6448
6449   InitUserDataDirectory();
6450
6451   if (!(file = fopen(filename, MODE_WRITE)))
6452   {
6453     Error(ERR_WARN, "cannot write setup file '%s'", filename);
6454     return;
6455   }
6456
6457   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
6458                                                getCookie("SETUP")));
6459   fprintf(file, "\n");
6460
6461   /* global setup */
6462   si = setup;
6463   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
6464   {
6465     /* just to make things nicer :) */
6466     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
6467         i == SETUP_TOKEN_GRAPHICS_SET)
6468       fprintf(file, "\n");
6469
6470     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
6471   }
6472
6473   /* editor setup */
6474   sei = setup.editor;
6475   fprintf(file, "\n");
6476   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
6477     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
6478
6479   /* shortcut setup */
6480   ssi = setup.shortcut;
6481   fprintf(file, "\n");
6482   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
6483     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
6484
6485   /* player setup */
6486   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
6487   {
6488     char prefix[30];
6489
6490     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
6491     fprintf(file, "\n");
6492
6493     sii = setup.input[pnr];
6494     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
6495       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
6496   }
6497
6498   /* system setup */
6499   syi = setup.system;
6500   fprintf(file, "\n");
6501   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
6502     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
6503
6504   /* options setup */
6505   soi = setup.options;
6506   fprintf(file, "\n");
6507   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
6508     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
6509
6510   fclose(file);
6511
6512   SetFilePermissions(filename, PERMS_PRIVATE);
6513 }
6514
6515 void SaveSetup_EditorCascade()
6516 {
6517   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
6518   FILE *file;
6519   int i;
6520
6521   InitUserDataDirectory();
6522
6523   if (!(file = fopen(filename, MODE_WRITE)))
6524   {
6525     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
6526     free(filename);
6527     return;
6528   }
6529
6530   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
6531                                                getCookie("SETUP")));
6532   fprintf(file, "\n");
6533
6534   seci = setup.editor_cascade;
6535   fprintf(file, "\n");
6536   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
6537     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
6538
6539   fclose(file);
6540
6541   SetFilePermissions(filename, PERMS_PRIVATE);
6542
6543   free(filename);
6544 }
6545
6546 void LoadCustomElementDescriptions()
6547 {
6548   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
6549   SetupFileHash *setup_file_hash;
6550   int i;
6551
6552   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6553   {
6554     if (element_info[i].custom_description != NULL)
6555     {
6556       free(element_info[i].custom_description);
6557       element_info[i].custom_description = NULL;
6558     }
6559   }
6560
6561   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
6562     return;
6563
6564   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6565   {
6566     char *token = getStringCat2(element_info[i].token_name, ".name");
6567     char *value = getHashEntry(setup_file_hash, token);
6568
6569     if (value != NULL)
6570       element_info[i].custom_description = getStringCopy(value);
6571
6572     free(token);
6573   }
6574
6575   freeSetupFileHash(setup_file_hash);
6576 }
6577
6578 static void LoadSpecialMenuDesignSettingsFromFilename(char *filename)
6579 {
6580   SetupFileHash *setup_file_hash;
6581   int i;
6582
6583 #if 0
6584   printf("LoadSpecialMenuDesignSettings from file '%s' ...\n", filename);
6585 #endif
6586
6587   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
6588     return;
6589
6590   /* special case: initialize with default values that may be overwritten */
6591   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
6592   {
6593     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
6594     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
6595     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
6596
6597     if (value_x != NULL)
6598       menu.draw_xoffset[i] = get_integer_from_string(value_x);
6599     if (value_y != NULL)
6600       menu.draw_yoffset[i] = get_integer_from_string(value_y);
6601     if (list_size != NULL)
6602       menu.list_size[i] = get_integer_from_string(list_size);
6603   }
6604
6605   /* read (and overwrite with) values that may be specified in config file */
6606   for (i = 0; image_config_vars[i].token != NULL; i++)
6607   {
6608     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
6609
6610     if (value != NULL)
6611       *image_config_vars[i].value =
6612         get_auto_parameter_value(image_config_vars[i].token, value);
6613   }
6614
6615   freeSetupFileHash(setup_file_hash);
6616 }
6617
6618 void LoadSpecialMenuDesignSettings()
6619 {
6620   char *filename_base = UNDEFINED_FILENAME, *filename_local;
6621   int i, j;
6622
6623   /* always start with reliable default values from default config */
6624   for (i = 0; image_config_vars[i].token != NULL; i++)
6625     for (j = 0; image_config[j].token != NULL; j++)
6626       if (strEqual(image_config_vars[i].token, image_config[j].token))
6627         *image_config_vars[i].value =
6628           get_auto_parameter_value(image_config_vars[i].token,
6629                                    image_config[j].value);
6630
6631 #if 1
6632   if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
6633   {
6634     /* first look for special settings configured in level series config */
6635     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
6636
6637     if (fileExists(filename_base))
6638       LoadSpecialMenuDesignSettingsFromFilename(filename_base);
6639   }
6640
6641   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
6642
6643   if (filename_local != NULL && !strEqual(filename_base, filename_local))
6644     LoadSpecialMenuDesignSettingsFromFilename(filename_local);
6645
6646 #else
6647
6648   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
6649
6650   LoadSpecialMenuDesignSettingsFromFilename(filename_local);
6651 #endif
6652 }
6653
6654 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
6655 {
6656   char *filename = getEditorSetupFilename();
6657   SetupFileList *setup_file_list, *list;
6658   SetupFileHash *element_hash;
6659   int num_unknown_tokens = 0;
6660   int i;
6661
6662   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
6663     return;
6664
6665   element_hash = newSetupFileHash();
6666
6667   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6668     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
6669
6670   /* determined size may be larger than needed (due to unknown elements) */
6671   *num_elements = 0;
6672   for (list = setup_file_list; list != NULL; list = list->next)
6673     (*num_elements)++;
6674
6675   /* add space for up to 3 more elements for padding that may be needed */
6676   *num_elements += 3;
6677
6678   /* free memory for old list of elements, if needed */
6679   checked_free(*elements);
6680
6681   /* allocate memory for new list of elements */
6682   *elements = checked_malloc(*num_elements * sizeof(int));
6683
6684   *num_elements = 0;
6685   for (list = setup_file_list; list != NULL; list = list->next)
6686   {
6687     char *value = getHashEntry(element_hash, list->token);
6688
6689     if (value == NULL)          /* try to find obsolete token mapping */
6690     {
6691       char *mapped_token = get_mapped_token(list->token);
6692
6693       if (mapped_token != NULL)
6694       {
6695         value = getHashEntry(element_hash, mapped_token);
6696
6697         free(mapped_token);
6698       }
6699     }
6700
6701     if (value != NULL)
6702     {
6703       (*elements)[(*num_elements)++] = atoi(value);
6704     }
6705     else
6706     {
6707       if (num_unknown_tokens == 0)
6708       {
6709         Error(ERR_RETURN_LINE, "-");
6710         Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
6711         Error(ERR_RETURN, "- config file: '%s'", filename);
6712
6713         num_unknown_tokens++;
6714       }
6715
6716       Error(ERR_RETURN, "- token: '%s'", list->token);
6717     }
6718   }
6719
6720   if (num_unknown_tokens > 0)
6721     Error(ERR_RETURN_LINE, "-");
6722
6723   while (*num_elements % 4)     /* pad with empty elements, if needed */
6724     (*elements)[(*num_elements)++] = EL_EMPTY;
6725
6726   freeSetupFileList(setup_file_list);
6727   freeSetupFileHash(element_hash);
6728
6729 #if 0
6730   for (i = 0; i < *num_elements; i++)
6731     printf("editor: element '%s' [%d]\n",
6732            element_info[(*elements)[i]].token_name, (*elements)[i]);
6733 #endif
6734 }
6735
6736 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
6737                                                      boolean is_sound)
6738 {
6739   SetupFileHash *setup_file_hash = NULL;
6740   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
6741   char *filename_music, *filename_prefix, *filename_info;
6742   struct
6743   {
6744     char *token;
6745     char **value_ptr;
6746   }
6747   token_to_value_ptr[] =
6748   {
6749     { "title_header",   &tmp_music_file_info.title_header       },
6750     { "artist_header",  &tmp_music_file_info.artist_header      },
6751     { "album_header",   &tmp_music_file_info.album_header       },
6752     { "year_header",    &tmp_music_file_info.year_header        },
6753
6754     { "title",          &tmp_music_file_info.title              },
6755     { "artist",         &tmp_music_file_info.artist             },
6756     { "album",          &tmp_music_file_info.album              },
6757     { "year",           &tmp_music_file_info.year               },
6758
6759     { NULL,             NULL                                    },
6760   };
6761   int i;
6762
6763   filename_music = (is_sound ? getCustomSoundFilename(basename) :
6764                     getCustomMusicFilename(basename));
6765
6766   if (filename_music == NULL)
6767     return NULL;
6768
6769   /* ---------- try to replace file extension ---------- */
6770
6771   filename_prefix = getStringCopy(filename_music);
6772   if (strrchr(filename_prefix, '.') != NULL)
6773     *strrchr(filename_prefix, '.') = '\0';
6774   filename_info = getStringCat2(filename_prefix, ".txt");
6775
6776 #if 0
6777   printf("trying to load file '%s'...\n", filename_info);
6778 #endif
6779
6780   if (fileExists(filename_info))
6781     setup_file_hash = loadSetupFileHash(filename_info);
6782
6783   free(filename_prefix);
6784   free(filename_info);
6785
6786   if (setup_file_hash == NULL)
6787   {
6788     /* ---------- try to add file extension ---------- */
6789
6790     filename_prefix = getStringCopy(filename_music);
6791     filename_info = getStringCat2(filename_prefix, ".txt");
6792
6793 #if 0
6794     printf("trying to load file '%s'...\n", filename_info);
6795 #endif
6796
6797     if (fileExists(filename_info))
6798       setup_file_hash = loadSetupFileHash(filename_info);
6799
6800     free(filename_prefix);
6801     free(filename_info);
6802   }
6803
6804   if (setup_file_hash == NULL)
6805     return NULL;
6806
6807   /* ---------- music file info found ---------- */
6808
6809   memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
6810
6811   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
6812   {
6813     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
6814
6815     *token_to_value_ptr[i].value_ptr =
6816       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
6817   }
6818
6819   tmp_music_file_info.basename = getStringCopy(basename);
6820   tmp_music_file_info.music = music;
6821   tmp_music_file_info.is_sound = is_sound;
6822
6823   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
6824   *new_music_file_info = tmp_music_file_info;
6825
6826   return new_music_file_info;
6827 }
6828
6829 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
6830 {
6831   return get_music_file_info_ext(basename, music, FALSE);
6832 }
6833
6834 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
6835 {
6836   return get_music_file_info_ext(basename, sound, TRUE);
6837 }
6838
6839 static boolean music_info_listed_ext(struct MusicFileInfo *list,
6840                                      char *basename, boolean is_sound)
6841 {
6842   for (; list != NULL; list = list->next)
6843     if (list->is_sound == is_sound && strEqual(list->basename, basename))
6844       return TRUE;
6845
6846   return FALSE;
6847 }
6848
6849 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
6850 {
6851   return music_info_listed_ext(list, basename, FALSE);
6852 }
6853
6854 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
6855 {
6856   return music_info_listed_ext(list, basename, TRUE);
6857 }
6858
6859 void LoadMusicInfo()
6860 {
6861   char *music_directory = getCustomMusicDirectory();
6862   int num_music = getMusicListSize();
6863   int num_music_noconf = 0;
6864   int num_sounds = getSoundListSize();
6865   DIR *dir;
6866   struct dirent *dir_entry;
6867   struct FileInfo *music, *sound;
6868   struct MusicFileInfo *next, **new;
6869   int i;
6870
6871   while (music_file_info != NULL)
6872   {
6873     next = music_file_info->next;
6874
6875     checked_free(music_file_info->basename);
6876
6877     checked_free(music_file_info->title_header);
6878     checked_free(music_file_info->artist_header);
6879     checked_free(music_file_info->album_header);
6880     checked_free(music_file_info->year_header);
6881
6882     checked_free(music_file_info->title);
6883     checked_free(music_file_info->artist);
6884     checked_free(music_file_info->album);
6885     checked_free(music_file_info->year);
6886
6887     free(music_file_info);
6888
6889     music_file_info = next;
6890   }
6891
6892   new = &music_file_info;
6893
6894   for (i = 0; i < num_music; i++)
6895   {
6896     music = getMusicListEntry(i);
6897
6898     if (music->filename == NULL)
6899       continue;
6900
6901     if (strEqual(music->filename, UNDEFINED_FILENAME))
6902       continue;
6903
6904     /* a configured file may be not recognized as music */
6905     if (!FileIsMusic(music->filename))
6906       continue;
6907
6908 #if 0
6909     printf("::: -> '%s' (configured)\n", music->filename);
6910 #endif
6911
6912     if (!music_info_listed(music_file_info, music->filename))
6913     {
6914       *new = get_music_file_info(music->filename, i);
6915       if (*new != NULL)
6916         new = &(*new)->next;
6917     }
6918   }
6919
6920   if ((dir = opendir(music_directory)) == NULL)
6921   {
6922     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
6923     return;
6924   }
6925
6926   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
6927   {
6928     char *basename = dir_entry->d_name;
6929     boolean music_already_used = FALSE;
6930     int i;
6931
6932     /* skip all music files that are configured in music config file */
6933     for (i = 0; i < num_music; i++)
6934     {
6935       music = getMusicListEntry(i);
6936
6937       if (music->filename == NULL)
6938         continue;
6939
6940       if (strEqual(basename, music->filename))
6941       {
6942         music_already_used = TRUE;
6943         break;
6944       }
6945     }
6946
6947     if (music_already_used)
6948       continue;
6949
6950     if (!FileIsMusic(basename))
6951       continue;
6952
6953 #if 0
6954     printf("::: -> '%s' (found in directory)\n", basename);
6955 #endif
6956
6957     if (!music_info_listed(music_file_info, basename))
6958     {
6959       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
6960       if (*new != NULL)
6961         new = &(*new)->next;
6962     }
6963
6964     num_music_noconf++;
6965   }
6966
6967   closedir(dir);
6968
6969   for (i = 0; i < num_sounds; i++)
6970   {
6971     sound = getSoundListEntry(i);
6972
6973     if (sound->filename == NULL)
6974       continue;
6975
6976     if (strEqual(sound->filename, UNDEFINED_FILENAME))
6977       continue;
6978
6979     /* a configured file may be not recognized as sound */
6980     if (!FileIsSound(sound->filename))
6981       continue;
6982
6983 #if 0
6984     printf("::: -> '%s' (configured)\n", sound->filename);
6985 #endif
6986
6987     if (!sound_info_listed(music_file_info, sound->filename))
6988     {
6989       *new = get_sound_file_info(sound->filename, i);
6990       if (*new != NULL)
6991         new = &(*new)->next;
6992     }
6993   }
6994
6995 #if 0
6996   for (next = music_file_info; next != NULL; next = next->next)
6997     printf("::: title == '%s'\n", next->title);
6998 #endif
6999 }
7000
7001 void add_helpanim_entry(int element, int action, int direction, int delay,
7002                         int *num_list_entries)
7003 {
7004   struct HelpAnimInfo *new_list_entry;
7005   (*num_list_entries)++;
7006
7007   helpanim_info =
7008     checked_realloc(helpanim_info,
7009                     *num_list_entries * sizeof(struct HelpAnimInfo));
7010   new_list_entry = &helpanim_info[*num_list_entries - 1];
7011
7012   new_list_entry->element = element;
7013   new_list_entry->action = action;
7014   new_list_entry->direction = direction;
7015   new_list_entry->delay = delay;
7016 }
7017
7018 void print_unknown_token(char *filename, char *token, int token_nr)
7019 {
7020   if (token_nr == 0)
7021   {
7022     Error(ERR_RETURN_LINE, "-");
7023     Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
7024     Error(ERR_RETURN, "- config file: '%s'", filename);
7025   }
7026
7027   Error(ERR_RETURN, "- token: '%s'", token);
7028 }
7029
7030 void print_unknown_token_end(int token_nr)
7031 {
7032   if (token_nr > 0)
7033     Error(ERR_RETURN_LINE, "-");
7034 }
7035
7036 void LoadHelpAnimInfo()
7037 {
7038   char *filename = getHelpAnimFilename();
7039   SetupFileList *setup_file_list = NULL, *list;
7040   SetupFileHash *element_hash, *action_hash, *direction_hash;
7041   int num_list_entries = 0;
7042   int num_unknown_tokens = 0;
7043   int i;
7044
7045   if (fileExists(filename))
7046     setup_file_list = loadSetupFileList(filename);
7047
7048   if (setup_file_list == NULL)
7049   {
7050     /* use reliable default values from static configuration */
7051     SetupFileList *insert_ptr;
7052
7053     insert_ptr = setup_file_list =
7054       newSetupFileList(helpanim_config[0].token,
7055                        helpanim_config[0].value);
7056
7057     for (i = 1; helpanim_config[i].token; i++)
7058       insert_ptr = addListEntry(insert_ptr,
7059                                 helpanim_config[i].token,
7060                                 helpanim_config[i].value);
7061   }
7062
7063   element_hash   = newSetupFileHash();
7064   action_hash    = newSetupFileHash();
7065   direction_hash = newSetupFileHash();
7066
7067   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
7068     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
7069
7070   for (i = 0; i < NUM_ACTIONS; i++)
7071     setHashEntry(action_hash, element_action_info[i].suffix,
7072                  i_to_a(element_action_info[i].value));
7073
7074   /* do not store direction index (bit) here, but direction value! */
7075   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
7076     setHashEntry(direction_hash, element_direction_info[i].suffix,
7077                  i_to_a(1 << element_direction_info[i].value));
7078
7079   for (list = setup_file_list; list != NULL; list = list->next)
7080   {
7081     char *element_token, *action_token, *direction_token;
7082     char *element_value, *action_value, *direction_value;
7083     int delay = atoi(list->value);
7084
7085     if (strEqual(list->token, "end"))
7086     {
7087       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
7088
7089       continue;
7090     }
7091
7092     /* first try to break element into element/action/direction parts;
7093        if this does not work, also accept combined "element[.act][.dir]"
7094        elements (like "dynamite.active"), which are unique elements */
7095
7096     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
7097     {
7098       element_value = getHashEntry(element_hash, list->token);
7099       if (element_value != NULL)        /* element found */
7100         add_helpanim_entry(atoi(element_value), -1, -1, delay,
7101                            &num_list_entries);
7102       else
7103       {
7104         /* no further suffixes found -- this is not an element */
7105         print_unknown_token(filename, list->token, num_unknown_tokens++);
7106       }
7107
7108       continue;
7109     }
7110
7111     /* token has format "<prefix>.<something>" */
7112
7113     action_token = strchr(list->token, '.');    /* suffix may be action ... */
7114     direction_token = action_token;             /* ... or direction */
7115
7116     element_token = getStringCopy(list->token);
7117     *strchr(element_token, '.') = '\0';
7118
7119     element_value = getHashEntry(element_hash, element_token);
7120
7121     if (element_value == NULL)          /* this is no element */
7122     {
7123       element_value = getHashEntry(element_hash, list->token);
7124       if (element_value != NULL)        /* combined element found */
7125         add_helpanim_entry(atoi(element_value), -1, -1, delay,
7126                            &num_list_entries);
7127       else
7128         print_unknown_token(filename, list->token, num_unknown_tokens++);
7129
7130       free(element_token);
7131
7132       continue;
7133     }
7134
7135     action_value = getHashEntry(action_hash, action_token);
7136
7137     if (action_value != NULL)           /* action found */
7138     {
7139       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
7140                     &num_list_entries);
7141
7142       free(element_token);
7143
7144       continue;
7145     }
7146
7147     direction_value = getHashEntry(direction_hash, direction_token);
7148
7149     if (direction_value != NULL)        /* direction found */
7150     {
7151       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
7152                          &num_list_entries);
7153
7154       free(element_token);
7155
7156       continue;
7157     }
7158
7159     if (strchr(action_token + 1, '.') == NULL)
7160     {
7161       /* no further suffixes found -- this is not an action nor direction */
7162
7163       element_value = getHashEntry(element_hash, list->token);
7164       if (element_value != NULL)        /* combined element found */
7165         add_helpanim_entry(atoi(element_value), -1, -1, delay,
7166                            &num_list_entries);
7167       else
7168         print_unknown_token(filename, list->token, num_unknown_tokens++);
7169
7170       free(element_token);
7171
7172       continue;
7173     }
7174
7175     /* token has format "<prefix>.<suffix>.<something>" */
7176
7177     direction_token = strchr(action_token + 1, '.');
7178
7179     action_token = getStringCopy(action_token);
7180     *strchr(action_token + 1, '.') = '\0';
7181
7182     action_value = getHashEntry(action_hash, action_token);
7183
7184     if (action_value == NULL)           /* this is no action */
7185     {
7186       element_value = getHashEntry(element_hash, list->token);
7187       if (element_value != NULL)        /* combined element found */
7188         add_helpanim_entry(atoi(element_value), -1, -1, delay,
7189                            &num_list_entries);
7190       else
7191         print_unknown_token(filename, list->token, num_unknown_tokens++);
7192
7193       free(element_token);
7194       free(action_token);
7195
7196       continue;
7197     }
7198
7199     direction_value = getHashEntry(direction_hash, direction_token);
7200
7201     if (direction_value != NULL)        /* direction found */
7202     {
7203       add_helpanim_entry(atoi(element_value), atoi(action_value),
7204                          atoi(direction_value), delay, &num_list_entries);
7205
7206       free(element_token);
7207       free(action_token);
7208
7209       continue;
7210     }
7211
7212     /* this is no direction */
7213
7214     element_value = getHashEntry(element_hash, list->token);
7215     if (element_value != NULL)          /* combined element found */
7216       add_helpanim_entry(atoi(element_value), -1, -1, delay,
7217                          &num_list_entries);
7218     else
7219       print_unknown_token(filename, list->token, num_unknown_tokens++);
7220
7221     free(element_token);
7222     free(action_token);
7223   }
7224
7225   print_unknown_token_end(num_unknown_tokens);
7226
7227   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
7228   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
7229
7230   freeSetupFileList(setup_file_list);
7231   freeSetupFileHash(element_hash);
7232   freeSetupFileHash(action_hash);
7233   freeSetupFileHash(direction_hash);
7234
7235 #if 0
7236   for (i = 0; i < num_list_entries; i++)
7237     printf("::: '%s': %d, %d, %d => %d\n",
7238            EL_NAME(helpanim_info[i].element),
7239            helpanim_info[i].element,
7240            helpanim_info[i].action,
7241            helpanim_info[i].direction,
7242            helpanim_info[i].delay);
7243 #endif
7244 }
7245
7246 void LoadHelpTextInfo()
7247 {
7248   char *filename = getHelpTextFilename();
7249   int i;
7250
7251   if (helptext_info != NULL)
7252   {
7253     freeSetupFileHash(helptext_info);
7254     helptext_info = NULL;
7255   }
7256
7257   if (fileExists(filename))
7258     helptext_info = loadSetupFileHash(filename);
7259
7260   if (helptext_info == NULL)
7261   {
7262     /* use reliable default values from static configuration */
7263     helptext_info = newSetupFileHash();
7264
7265     for (i = 0; helptext_config[i].token; i++)
7266       setHashEntry(helptext_info,
7267                    helptext_config[i].token,
7268                    helptext_config[i].value);
7269   }
7270
7271 #if 0
7272   BEGIN_HASH_ITERATION(helptext_info, itr)
7273   {
7274     printf("::: '%s' => '%s'\n",
7275            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
7276   }
7277   END_HASH_ITERATION(hash, itr)
7278 #endif
7279 }
7280
7281
7282 /* ------------------------------------------------------------------------- *
7283  * convert levels
7284  * ------------------------------------------------------------------------- */
7285
7286 #define MAX_NUM_CONVERT_LEVELS          1000
7287
7288 void ConvertLevels()
7289 {
7290   static LevelDirTree *convert_leveldir = NULL;
7291   static int convert_level_nr = -1;
7292   static int num_levels_handled = 0;
7293   static int num_levels_converted = 0;
7294   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
7295   int i;
7296
7297   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7298                                                global.convert_leveldir);
7299
7300   if (convert_leveldir == NULL)
7301     Error(ERR_EXIT, "no such level identifier: '%s'",
7302           global.convert_leveldir);
7303
7304   leveldir_current = convert_leveldir;
7305
7306   if (global.convert_level_nr != -1)
7307   {
7308     convert_leveldir->first_level = global.convert_level_nr;
7309     convert_leveldir->last_level  = global.convert_level_nr;
7310   }
7311
7312   convert_level_nr = convert_leveldir->first_level;
7313
7314   printf_line("=", 79);
7315   printf("Converting levels\n");
7316   printf_line("-", 79);
7317   printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
7318   printf("Level series name:       '%s'\n", convert_leveldir->name);
7319   printf("Level series author:     '%s'\n", convert_leveldir->author);
7320   printf("Number of levels:        %d\n",   convert_leveldir->levels);
7321   printf_line("=", 79);
7322   printf("\n");
7323
7324   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
7325     levels_failed[i] = FALSE;
7326
7327   while (convert_level_nr <= convert_leveldir->last_level)
7328   {
7329     char *level_filename;
7330     boolean new_level;
7331
7332     level_nr = convert_level_nr++;
7333
7334     printf("Level %03d: ", level_nr);
7335
7336     LoadLevel(level_nr);
7337     if (level.no_valid_file)
7338     {
7339       printf("(no level)\n");
7340       continue;
7341     }
7342
7343     printf("converting level ... ");
7344
7345     level_filename = getDefaultLevelFilename(level_nr);
7346     new_level = !fileExists(level_filename);
7347
7348     if (new_level)
7349     {
7350       SaveLevel(level_nr);
7351
7352       num_levels_converted++;
7353
7354       printf("converted.\n");
7355     }
7356     else
7357     {
7358       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
7359         levels_failed[level_nr] = TRUE;
7360
7361       printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
7362     }
7363
7364     num_levels_handled++;
7365   }
7366
7367   printf("\n");
7368   printf_line("=", 79);
7369   printf("Number of levels handled: %d\n", num_levels_handled);
7370   printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
7371          (num_levels_handled ?
7372           num_levels_converted * 100 / num_levels_handled : 0));
7373   printf_line("-", 79);
7374   printf("Summary (for automatic parsing by scripts):\n");
7375   printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
7376          convert_leveldir->identifier, num_levels_converted,
7377          num_levels_handled,
7378          (num_levels_handled ?
7379           num_levels_converted * 100 / num_levels_handled : 0));
7380
7381   if (num_levels_handled != num_levels_converted)
7382   {
7383     printf(", FAILED:");
7384     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
7385       if (levels_failed[i])
7386         printf(" %03d", i);
7387   }
7388
7389   printf("\n");
7390   printf_line("=", 79);
7391
7392   CloseAllAndExit(0);
7393 }