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