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