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