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