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