16af2dd4b4d84769faffc1c981a4153040a98498
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "tools.h"
22 #include "tape.h"
23
24
25 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
26 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
27 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
28
29 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
30 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
31
32 #define LEVEL_CHUNK_VERS_SIZE   8       /* size of file version chunk */
33 #define LEVEL_CHUNK_DATE_SIZE   4       /* size of file date chunk    */
34 #define LEVEL_CHUNK_HEAD_SIZE   80      /* size of level file header  */
35 #define LEVEL_CHUNK_HEAD_UNUSED 0       /* unused level header bytes  */
36 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
37 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
38 #define LEVEL_CHUNK_CNT3_HEADER 16      /* size of level CNT3 header  */
39 #define LEVEL_CHUNK_CNT3_UNUSED 10      /* unused CNT3 chunk bytes    */
40 #define LEVEL_CPART_CUS3_SIZE   134     /* size of CUS3 chunk part    */
41 #define LEVEL_CPART_CUS3_UNUSED 15      /* unused CUS3 bytes / part   */
42 #define LEVEL_CHUNK_GRP1_SIZE   74      /* size of level GRP1 chunk   */
43
44 /* (element number, number of change pages, change page number) */
45 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
46
47 /* (element number only) */
48 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
49 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
50
51 /* (nothing at all if unchanged) */
52 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
53
54 #define TAPE_CHUNK_VERS_SIZE    8       /* size of file version chunk */
55 #define TAPE_CHUNK_HEAD_SIZE    20      /* size of tape file header   */
56 #define TAPE_CHUNK_HEAD_UNUSED  3       /* unused tape header bytes   */
57
58 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
59 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
60 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
61
62 /* file identifier strings */
63 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
64 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
65 #define SCORE_COOKIE                    "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
66
67 /* values for deciding when (not) to save configuration data */
68 #define SAVE_CONF_NEVER                 0
69 #define SAVE_CONF_ALWAYS                1
70 #define SAVE_CONF_WHEN_CHANGED          -1
71
72 /* values for chunks using micro chunks */
73 #define CONF_MASK_1_BYTE                0x00
74 #define CONF_MASK_2_BYTE                0x40
75 #define CONF_MASK_4_BYTE                0x80
76 #define CONF_MASK_MULTI_BYTES           0xc0
77
78 #define CONF_MASK_BYTES                 0xc0
79 #define CONF_MASK_TOKEN                 0x3f
80
81 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
82 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
83 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
84 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
85
86 /* these definitions are just for convenience of use and readability */
87 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
88 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
89 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
90 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
91
92 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
93                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
94                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
95
96 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
97 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
98 #define CONF_ELEMENT_NUM_BYTES          (2)
99
100 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
101                                          (t) == TYPE_ELEMENT_LIST ?     \
102                                          CONF_ELEMENT_NUM_BYTES :       \
103                                          (t) == TYPE_CONTENT ||         \
104                                          (t) == TYPE_CONTENT_LIST ?     \
105                                          CONF_CONTENT_NUM_BYTES : 1)
106
107 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
108 #define CONF_ELEMENTS_ELEMENT(b,i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) |  \
109                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
110
111 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
112                                          (y) * 3 + (x))
113 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
114                                          CONF_ELEMENT_NUM_BYTES)
115 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
116                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
117
118 /* temporary variables used to store pointers to structure members */
119 static struct LevelInfo li;
120 static struct ElementInfo xx_ei, yy_ei;
121 static struct ElementChangeInfo xx_change;
122 static struct ElementGroupInfo xx_group;
123 static struct EnvelopeInfo xx_envelope;
124 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
125 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
126 static int xx_num_contents;
127 static int xx_current_change_page;
128 static char xx_default_string_empty[1] = "";
129 static int xx_string_length_unused;
130
131 struct LevelFileConfigInfo
132 {
133   int element;                  /* element for which data is to be stored */
134   int save_type;                /* save data always, never or when changed */
135   int data_type;                /* data type (used internally, not stored) */
136   int conf_type;                /* micro chunk identifier (stored in file) */
137
138   /* (mandatory) */
139   void *value;                  /* variable that holds the data to be stored */
140   int default_value;            /* initial default value for this variable */
141
142   /* (optional) */
143   void *value_copy;             /* variable that holds the data to be copied */
144   void *num_entities;           /* number of entities for multi-byte data */
145   int default_num_entities;     /* default number of entities for this data */
146   int max_num_entities;         /* maximal number of entities for this data */
147   char *default_string;         /* optional default string for string data */
148 };
149
150 static struct LevelFileConfigInfo chunk_config_INFO[] =
151 {
152   /* ---------- values not related to single elements ----------------------- */
153
154   {
155     -1,                                 SAVE_CONF_ALWAYS,
156     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
157     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
158   },
159
160   {
161     -1,                                 SAVE_CONF_ALWAYS,
162     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
163     &li.fieldx,                         STD_LEV_FIELDX
164   },
165   {
166     -1,                                 SAVE_CONF_ALWAYS,
167     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
168     &li.fieldy,                         STD_LEV_FIELDY
169   },
170
171   {
172     -1,                                 SAVE_CONF_ALWAYS,
173     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
174     &li.time,                           100
175   },
176
177   {
178     -1,                                 SAVE_CONF_ALWAYS,
179     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
180     &li.gems_needed,                    0
181   },
182
183   {
184     -1,                                 -1,
185     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
186     &li.random_seed,                    0
187   },
188
189   {
190     -1,                                 -1,
191     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
192     &li.use_step_counter,               FALSE
193   },
194
195   {
196     -1,                                 -1,
197     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
198     &li.wind_direction_initial,         MV_NONE
199   },
200
201   {
202     -1,                                 -1,
203     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
204     &li.em_slippery_gems,               FALSE
205   },
206
207   {
208     -1,                                 -1,
209     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
210     &li.use_custom_template,            FALSE
211   },
212
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      /* default: everything can */
217   },
218
219   {
220     -1,                                 -1,
221     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
222     &li.dont_collide_with_bits,         ~0      /* default: always deadly */
223   },
224
225   {
226     -1,                                 -1,
227     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
228     &li.em_explodes_by_fire,            FALSE
229   },
230
231   {
232     -1,                                 -1,
233     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
234     &li.score[SC_TIME_BONUS],           1
235   },
236
237   {
238     -1,                                 -1,
239     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
240     &li.auto_exit_sokoban,              FALSE
241   },
242
243   {
244     -1,                                 -1,
245     -1,                                 -1,
246     NULL,                               -1
247   }
248 };
249
250 static struct LevelFileConfigInfo chunk_config_ELEM[] =
251 {
252   /* (these values are the same for each player) */
253   {
254     EL_PLAYER_1,                        -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
256     &li.block_last_field,               FALSE   /* default case for EM levels */
257   },
258   {
259     EL_PLAYER_1,                        -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
261     &li.sp_block_last_field,            TRUE    /* default case for SP levels */
262   },
263   {
264     EL_PLAYER_1,                        -1,
265     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
266     &li.instant_relocation,             FALSE
267   },
268   {
269     EL_PLAYER_1,                        -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
271     &li.can_pass_to_walkable,           FALSE
272   },
273   {
274     EL_PLAYER_1,                        -1,
275     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
276     &li.block_snap_field,               TRUE
277   },
278   {
279     EL_PLAYER_1,                        -1,
280     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
281     &li.continuous_snapping,            TRUE
282   },
283   {
284     EL_PLAYER_1,                        -1,
285     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
286     &li.shifted_relocation,             FALSE
287   },
288
289   /* (these values are different for each player) */
290   {
291     EL_PLAYER_1,                        -1,
292     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
293     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
294   },
295   {
296     EL_PLAYER_1,                        -1,
297     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
298     &li.initial_player_gravity[0],      FALSE
299   },
300   {
301     EL_PLAYER_1,                        -1,
302     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
303     &li.use_start_element[0],           FALSE
304   },
305   {
306     EL_PLAYER_1,                        -1,
307     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
308     &li.start_element[0],               EL_PLAYER_1
309   },
310   {
311     EL_PLAYER_1,                        -1,
312     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
313     &li.use_artwork_element[0],         FALSE
314   },
315   {
316     EL_PLAYER_1,                        -1,
317     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
318     &li.artwork_element[0],             EL_PLAYER_1
319   },
320   {
321     EL_PLAYER_1,                        -1,
322     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
323     &li.use_explosion_element[0],       FALSE
324   },
325   {
326     EL_PLAYER_1,                        -1,
327     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
328     &li.explosion_element[0],           EL_PLAYER_1
329   },
330   {
331     EL_PLAYER_1,                        -1,
332     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
333     &li.use_initial_inventory[0],       FALSE
334   },
335   {
336     EL_PLAYER_1,                        -1,
337     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
338     &li.initial_inventory_size[0],      1
339   },
340   {
341     EL_PLAYER_1,                        -1,
342     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
343     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
344     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
345   },
346
347   {
348     EL_PLAYER_2,                        -1,
349     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
350     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
351   },
352   {
353     EL_PLAYER_2,                        -1,
354     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
355     &li.initial_player_gravity[1],      FALSE
356   },
357   {
358     EL_PLAYER_2,                        -1,
359     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
360     &li.use_start_element[1],           FALSE
361   },
362   {
363     EL_PLAYER_2,                        -1,
364     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
365     &li.start_element[1],               EL_PLAYER_2
366   },
367   {
368     EL_PLAYER_2,                        -1,
369     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
370     &li.use_artwork_element[1],         FALSE
371   },
372   {
373     EL_PLAYER_2,                        -1,
374     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
375     &li.artwork_element[1],             EL_PLAYER_2
376   },
377   {
378     EL_PLAYER_2,                        -1,
379     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
380     &li.use_explosion_element[1],       FALSE
381   },
382   {
383     EL_PLAYER_2,                        -1,
384     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
385     &li.explosion_element[1],           EL_PLAYER_2
386   },
387   {
388     EL_PLAYER_2,                        -1,
389     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
390     &li.use_initial_inventory[1],       FALSE
391   },
392   {
393     EL_PLAYER_2,                        -1,
394     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
395     &li.initial_inventory_size[1],      1
396   },
397   {
398     EL_PLAYER_2,                        -1,
399     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
400     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
401     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
402   },
403
404   {
405     EL_PLAYER_3,                        -1,
406     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
407     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
408   },
409   {
410     EL_PLAYER_3,                        -1,
411     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
412     &li.initial_player_gravity[2],      FALSE
413   },
414   {
415     EL_PLAYER_3,                        -1,
416     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
417     &li.use_start_element[2],           FALSE
418   },
419   {
420     EL_PLAYER_3,                        -1,
421     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
422     &li.start_element[2],               EL_PLAYER_3
423   },
424   {
425     EL_PLAYER_3,                        -1,
426     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
427     &li.use_artwork_element[2],         FALSE
428   },
429   {
430     EL_PLAYER_3,                        -1,
431     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
432     &li.artwork_element[2],             EL_PLAYER_3
433   },
434   {
435     EL_PLAYER_3,                        -1,
436     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
437     &li.use_explosion_element[2],       FALSE
438   },
439   {
440     EL_PLAYER_3,                        -1,
441     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
442     &li.explosion_element[2],           EL_PLAYER_3
443   },
444   {
445     EL_PLAYER_3,                        -1,
446     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
447     &li.use_initial_inventory[2],       FALSE
448   },
449   {
450     EL_PLAYER_3,                        -1,
451     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
452     &li.initial_inventory_size[2],      1
453   },
454   {
455     EL_PLAYER_3,                        -1,
456     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
457     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
458     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
459   },
460
461   {
462     EL_PLAYER_4,                        -1,
463     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
464     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
465   },
466   {
467     EL_PLAYER_4,                        -1,
468     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
469     &li.initial_player_gravity[3],      FALSE
470   },
471   {
472     EL_PLAYER_4,                        -1,
473     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
474     &li.use_start_element[3],           FALSE
475   },
476   {
477     EL_PLAYER_4,                        -1,
478     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
479     &li.start_element[3],               EL_PLAYER_4
480   },
481   {
482     EL_PLAYER_4,                        -1,
483     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
484     &li.use_artwork_element[3],         FALSE
485   },
486   {
487     EL_PLAYER_4,                        -1,
488     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
489     &li.artwork_element[3],             EL_PLAYER_4
490   },
491   {
492     EL_PLAYER_4,                        -1,
493     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
494     &li.use_explosion_element[3],       FALSE
495   },
496   {
497     EL_PLAYER_4,                        -1,
498     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
499     &li.explosion_element[3],           EL_PLAYER_4
500   },
501   {
502     EL_PLAYER_4,                        -1,
503     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
504     &li.use_initial_inventory[3],       FALSE
505   },
506   {
507     EL_PLAYER_4,                        -1,
508     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
509     &li.initial_inventory_size[3],      1
510   },
511   {
512     EL_PLAYER_4,                        -1,
513     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
514     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
515     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
516   },
517
518   {
519     EL_EMERALD,                         -1,
520     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
521     &li.score[SC_EMERALD],              10
522   },
523
524   {
525     EL_DIAMOND,                         -1,
526     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
527     &li.score[SC_DIAMOND],              10
528   },
529
530   {
531     EL_BUG,                             -1,
532     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
533     &li.score[SC_BUG],                  10
534   },
535
536   {
537     EL_SPACESHIP,                       -1,
538     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
539     &li.score[SC_SPACESHIP],            10
540   },
541
542   {
543     EL_PACMAN,                          -1,
544     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
545     &li.score[SC_PACMAN],               10
546   },
547
548   {
549     EL_NUT,                             -1,
550     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
551     &li.score[SC_NUT],                  10
552   },
553
554   {
555     EL_DYNAMITE,                        -1,
556     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
557     &li.score[SC_DYNAMITE],             10
558   },
559
560   {
561     EL_KEY_1,                           -1,
562     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
563     &li.score[SC_KEY],                  10
564   },
565
566   {
567     EL_PEARL,                           -1,
568     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
569     &li.score[SC_PEARL],                10
570   },
571
572   {
573     EL_CRYSTAL,                         -1,
574     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
575     &li.score[SC_CRYSTAL],              10
576   },
577
578   {
579     EL_BD_AMOEBA,                       -1,
580     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
581     &li.amoeba_content,                 EL_DIAMOND
582   },
583   {
584     EL_BD_AMOEBA,                       -1,
585     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
586     &li.amoeba_speed,                   10
587   },
588   {
589     EL_BD_AMOEBA,                       -1,
590     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
591     &li.grow_into_diggable,             TRUE
592   },
593
594   {
595     EL_YAMYAM,                          -1,
596     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
597     &li.yamyam_content,                 EL_ROCK, NULL,
598     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
599   },
600   {
601     EL_YAMYAM,                          -1,
602     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
603     &li.score[SC_YAMYAM],               10
604   },
605
606   {
607     EL_ROBOT,                           -1,
608     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
609     &li.score[SC_ROBOT],                10
610   },
611   {
612     EL_ROBOT,                           -1,
613     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
614     &li.slurp_score,                    10
615   },
616
617   {
618     EL_ROBOT_WHEEL,                     -1,
619     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
620     &li.time_wheel,                     10
621   },
622
623   {
624     EL_MAGIC_WALL,                      -1,
625     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
626     &li.time_magic_wall,                10
627   },
628
629   {
630     EL_GAME_OF_LIFE,                    -1,
631     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
632     &li.game_of_life[0],                2
633   },
634   {
635     EL_GAME_OF_LIFE,                    -1,
636     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
637     &li.game_of_life[1],                3
638   },
639   {
640     EL_GAME_OF_LIFE,                    -1,
641     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
642     &li.game_of_life[2],                3
643   },
644   {
645     EL_GAME_OF_LIFE,                    -1,
646     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
647     &li.game_of_life[3],                3
648   },
649
650   {
651     EL_BIOMAZE,                         -1,
652     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
653     &li.biomaze[0],                     2
654   },
655   {
656     EL_BIOMAZE,                         -1,
657     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
658     &li.biomaze[1],                     3
659   },
660   {
661     EL_BIOMAZE,                         -1,
662     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
663     &li.biomaze[2],                     3
664   },
665   {
666     EL_BIOMAZE,                         -1,
667     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
668     &li.biomaze[3],                     3
669   },
670
671   {
672     EL_TIMEGATE_SWITCH,                 -1,
673     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
674     &li.time_timegate,                  10
675   },
676
677   {
678     EL_LIGHT_SWITCH_ACTIVE,             -1,
679     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
680     &li.time_light,                     10
681   },
682
683   {
684     EL_SHIELD_NORMAL,                   -1,
685     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
686     &li.shield_normal_time,             10
687   },
688   {
689     EL_SHIELD_NORMAL,                   -1,
690     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
691     &li.score[SC_SHIELD],               10
692   },
693
694   {
695     EL_SHIELD_DEADLY,                   -1,
696     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
697     &li.shield_deadly_time,             10
698   },
699   {
700     EL_SHIELD_DEADLY,                   -1,
701     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
702     &li.score[SC_SHIELD],               10
703   },
704
705   {
706     EL_EXTRA_TIME,                      -1,
707     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
708     &li.extra_time,                     10
709   },
710   {
711     EL_EXTRA_TIME,                      -1,
712     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
713     &li.extra_time_score,               10
714   },
715
716   {
717     EL_TIME_ORB_FULL,                   -1,
718     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
719     &li.time_orb_time,                  10
720   },
721   {
722     EL_TIME_ORB_FULL,                   -1,
723     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
724     &li.use_time_orb_bug,               FALSE
725   },
726
727   {
728     EL_SPRING,                          -1,
729     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
730     &li.use_spring_bug,                 FALSE
731   },
732
733   {
734     EL_EMC_ANDROID,                     -1,
735     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
736     &li.android_move_time,              10
737   },
738   {
739     EL_EMC_ANDROID,                     -1,
740     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
741     &li.android_clone_time,             10
742   },
743   {
744     EL_EMC_ANDROID,                     -1,
745     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
746     &li.android_clone_element[0],       EL_EMPTY, NULL,
747     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
748   },
749
750   {
751     EL_EMC_LENSES,                      -1,
752     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
753     &li.lenses_score,                   10
754   },
755   {
756     EL_EMC_LENSES,                      -1,
757     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
758     &li.lenses_time,                    10
759   },
760
761   {
762     EL_EMC_MAGNIFIER,                   -1,
763     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
764     &li.magnify_score,                  10
765   },
766   {
767     EL_EMC_MAGNIFIER,                   -1,
768     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
769     &li.magnify_time,                   10
770   },
771
772   {
773     EL_EMC_MAGIC_BALL,                  -1,
774     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
775     &li.ball_time,                      10
776   },
777   {
778     EL_EMC_MAGIC_BALL,                  -1,
779     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
780     &li.ball_random,                    FALSE
781   },
782   {
783     EL_EMC_MAGIC_BALL,                  -1,
784     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
785     &li.ball_state_initial,             FALSE
786   },
787   {
788     EL_EMC_MAGIC_BALL,                  -1,
789     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
790     &li.ball_content,                   EL_EMPTY, NULL,
791     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
792   },
793
794   /* ---------- unused values ----------------------------------------------- */
795
796   {
797     EL_UNKNOWN,                         SAVE_CONF_NEVER,
798     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
799     &li.score[SC_UNKNOWN_14],           10
800   },
801   {
802     EL_UNKNOWN,                         SAVE_CONF_NEVER,
803     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
804     &li.score[SC_UNKNOWN_15],           10
805   },
806
807   {
808     -1,                                 -1,
809     -1,                                 -1,
810     NULL,                               -1
811   }
812 };
813
814 static struct LevelFileConfigInfo chunk_config_NOTE[] =
815 {
816   {
817     -1,                                 -1,
818     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
819     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
820   },
821   {
822     -1,                                 -1,
823     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
824     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
825   },
826
827   {
828     -1,                                 -1,
829     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
830     &xx_envelope.autowrap,              FALSE
831   },
832   {
833     -1,                                 -1,
834     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
835     &xx_envelope.centered,              FALSE
836   },
837
838   {
839     -1,                                 -1,
840     TYPE_STRING,                        CONF_VALUE_BYTES(1),
841     &xx_envelope.text,                  -1, NULL,
842     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
843     &xx_default_string_empty[0]
844   },
845
846   {
847     -1,                                 -1,
848     -1,                                 -1,
849     NULL,                               -1
850   }
851 };
852
853 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
854 {
855   {
856     -1,                                 -1,
857     TYPE_STRING,                        CONF_VALUE_BYTES(1),
858     &xx_ei.description[0],              -1,
859     &yy_ei.description[0],
860     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
861     &xx_default_description[0]
862   },
863
864   {
865     -1,                                 -1,
866     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
867     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
868     &yy_ei.properties[EP_BITFIELD_BASE_NR]
869   },
870 #if 0
871   /* (reserved) */
872   {
873     -1,                                 -1,
874     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
875     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
876     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
877   },
878 #endif
879
880   {
881     -1,                                 -1,
882     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
883     &xx_ei.use_gfx_element,             FALSE,
884     &yy_ei.use_gfx_element
885   },
886   {
887     -1,                                 -1,
888     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
889     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
890     &yy_ei.gfx_element_initial
891   },
892
893   {
894     -1,                                 -1,
895     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
896     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
897     &yy_ei.access_direction
898   },
899
900   {
901     -1,                                 -1,
902     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
903     &xx_ei.collect_score_initial,       10,
904     &yy_ei.collect_score_initial
905   },
906   {
907     -1,                                 -1,
908     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
909     &xx_ei.collect_count_initial,       1,
910     &yy_ei.collect_count_initial
911   },
912
913   {
914     -1,                                 -1,
915     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
916     &xx_ei.ce_value_fixed_initial,      0,
917     &yy_ei.ce_value_fixed_initial
918   },
919   {
920     -1,                                 -1,
921     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
922     &xx_ei.ce_value_random_initial,     0,
923     &yy_ei.ce_value_random_initial
924   },
925   {
926     -1,                                 -1,
927     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
928     &xx_ei.use_last_ce_value,           FALSE,
929     &yy_ei.use_last_ce_value
930   },
931
932   {
933     -1,                                 -1,
934     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
935     &xx_ei.push_delay_fixed,            8,
936     &yy_ei.push_delay_fixed
937   },
938   {
939     -1,                                 -1,
940     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
941     &xx_ei.push_delay_random,           8,
942     &yy_ei.push_delay_random
943   },
944   {
945     -1,                                 -1,
946     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
947     &xx_ei.drop_delay_fixed,            0,
948     &yy_ei.drop_delay_fixed
949   },
950   {
951     -1,                                 -1,
952     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
953     &xx_ei.drop_delay_random,           0,
954     &yy_ei.drop_delay_random
955   },
956   {
957     -1,                                 -1,
958     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
959     &xx_ei.move_delay_fixed,            0,
960     &yy_ei.move_delay_fixed
961   },
962   {
963     -1,                                 -1,
964     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
965     &xx_ei.move_delay_random,           0,
966     &yy_ei.move_delay_random
967   },
968
969   {
970     -1,                                 -1,
971     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
972     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
973     &yy_ei.move_pattern
974   },
975   {
976     -1,                                 -1,
977     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
978     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
979     &yy_ei.move_direction_initial
980   },
981   {
982     -1,                                 -1,
983     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
984     &xx_ei.move_stepsize,               TILEX / 8,
985     &yy_ei.move_stepsize
986   },
987
988   {
989     -1,                                 -1,
990     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
991     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
992     &yy_ei.move_enter_element
993   },
994   {
995     -1,                                 -1,
996     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
997     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
998     &yy_ei.move_leave_element
999   },
1000   {
1001     -1,                                 -1,
1002     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1003     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1004     &yy_ei.move_leave_type
1005   },
1006
1007   {
1008     -1,                                 -1,
1009     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1010     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1011     &yy_ei.slippery_type
1012   },
1013
1014   {
1015     -1,                                 -1,
1016     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1017     &xx_ei.explosion_type,              EXPLODES_3X3,
1018     &yy_ei.explosion_type
1019   },
1020   {
1021     -1,                                 -1,
1022     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1023     &xx_ei.explosion_delay,             16,
1024     &yy_ei.explosion_delay
1025   },
1026   {
1027     -1,                                 -1,
1028     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1029     &xx_ei.ignition_delay,              8,
1030     &yy_ei.ignition_delay
1031   },
1032
1033   {
1034     -1,                                 -1,
1035     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1036     &xx_ei.content,                     EL_EMPTY_SPACE,
1037     &yy_ei.content,
1038     &xx_num_contents,                   1, 1
1039   },
1040
1041   /* ---------- "num_change_pages" must be the last entry ------------------- */
1042
1043   {
1044     -1,                                 SAVE_CONF_ALWAYS,
1045     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1046     &xx_ei.num_change_pages,            1,
1047     &yy_ei.num_change_pages
1048   },
1049
1050   {
1051     -1,                                 -1,
1052     -1,                                 -1,
1053     NULL,                               -1,
1054     NULL
1055   }
1056 };
1057
1058 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1059 {
1060   /* ---------- "current_change_page" must be the first entry --------------- */
1061
1062   {
1063     -1,                                 SAVE_CONF_ALWAYS,
1064     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1065     &xx_current_change_page,            -1
1066   },
1067
1068   /* ---------- (the remaining entries can be in any order) ----------------- */
1069
1070   {
1071     -1,                                 -1,
1072     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1073     &xx_change.can_change,              FALSE
1074   },
1075
1076   {
1077     -1,                                 -1,
1078     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1079     &xx_event_bits[0],                  0
1080   },
1081   {
1082     -1,                                 -1,
1083     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1084     &xx_event_bits[1],                  0
1085   },
1086
1087   {
1088     -1,                                 -1,
1089     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1090     &xx_change.trigger_player,          CH_PLAYER_ANY
1091   },
1092   {
1093     -1,                                 -1,
1094     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1095     &xx_change.trigger_side,            CH_SIDE_ANY
1096   },
1097   {
1098     -1,                                 -1,
1099     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1100     &xx_change.trigger_page,            CH_PAGE_ANY
1101   },
1102
1103   {
1104     -1,                                 -1,
1105     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1106     &xx_change.target_element,          EL_EMPTY_SPACE
1107   },
1108
1109   {
1110     -1,                                 -1,
1111     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1112     &xx_change.delay_fixed,             0
1113   },
1114   {
1115     -1,                                 -1,
1116     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1117     &xx_change.delay_random,            0
1118   },
1119   {
1120     -1,                                 -1,
1121     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1122     &xx_change.delay_frames,            FRAMES_PER_SECOND
1123   },
1124
1125   {
1126     -1,                                 -1,
1127     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1128     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1129   },
1130
1131   {
1132     -1,                                 -1,
1133     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1134     &xx_change.explode,                 FALSE
1135   },
1136   {
1137     -1,                                 -1,
1138     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1139     &xx_change.use_target_content,      FALSE
1140   },
1141   {
1142     -1,                                 -1,
1143     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1144     &xx_change.only_if_complete,        FALSE
1145   },
1146   {
1147     -1,                                 -1,
1148     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1149     &xx_change.use_random_replace,      FALSE
1150   },
1151   {
1152     -1,                                 -1,
1153     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1154     &xx_change.random_percentage,       100
1155   },
1156   {
1157     -1,                                 -1,
1158     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1159     &xx_change.replace_when,            CP_WHEN_EMPTY
1160   },
1161
1162   {
1163     -1,                                 -1,
1164     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1165     &xx_change.has_action,              FALSE
1166   },
1167   {
1168     -1,                                 -1,
1169     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1170     &xx_change.action_type,             CA_NO_ACTION
1171   },
1172   {
1173     -1,                                 -1,
1174     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1175     &xx_change.action_mode,             CA_MODE_UNDEFINED
1176   },
1177   {
1178     -1,                                 -1,
1179     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1180     &xx_change.action_arg,              CA_ARG_UNDEFINED
1181   },
1182
1183   {
1184     -1,                                 -1,
1185     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1186     &xx_change.action_element,          EL_EMPTY_SPACE
1187   },
1188
1189   {
1190     -1,                                 -1,
1191     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1192     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1193     &xx_num_contents,                   1, 1
1194   },
1195
1196   {
1197     -1,                                 -1,
1198     -1,                                 -1,
1199     NULL,                               -1
1200   }
1201 };
1202
1203 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1204 {
1205   {
1206     -1,                                 -1,
1207     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1208     &xx_ei.description[0],              -1, NULL,
1209     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1210     &xx_default_description[0]
1211   },
1212
1213   {
1214     -1,                                 -1,
1215     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1216     &xx_ei.use_gfx_element,             FALSE
1217   },
1218   {
1219     -1,                                 -1,
1220     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1221     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1222   },
1223
1224   {
1225     -1,                                 -1,
1226     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1227     &xx_group.choice_mode,              ANIM_RANDOM
1228   },
1229
1230   {
1231     -1,                                 -1,
1232     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1233     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1234     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1235   },
1236
1237   {
1238     -1,                                 -1,
1239     -1,                                 -1,
1240     NULL,                               -1
1241   }
1242 };
1243
1244 static struct LevelFileConfigInfo chunk_config_CONF[] =         /* (OBSOLETE) */
1245 {
1246   {
1247     EL_PLAYER_1,                        -1,
1248     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1249     &li.block_snap_field,               TRUE
1250   },
1251   {
1252     EL_PLAYER_1,                        -1,
1253     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1254     &li.continuous_snapping,            TRUE
1255   },
1256   {
1257     EL_PLAYER_1,                        -1,
1258     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1259     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1260   },
1261   {
1262     EL_PLAYER_1,                        -1,
1263     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1264     &li.use_start_element[0],           FALSE
1265   },
1266   {
1267     EL_PLAYER_1,                        -1,
1268     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1269     &li.start_element[0],               EL_PLAYER_1
1270   },
1271   {
1272     EL_PLAYER_1,                        -1,
1273     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1274     &li.use_artwork_element[0],         FALSE
1275   },
1276   {
1277     EL_PLAYER_1,                        -1,
1278     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1279     &li.artwork_element[0],             EL_PLAYER_1
1280   },
1281   {
1282     EL_PLAYER_1,                        -1,
1283     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1284     &li.use_explosion_element[0],       FALSE
1285   },
1286   {
1287     EL_PLAYER_1,                        -1,
1288     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1289     &li.explosion_element[0],           EL_PLAYER_1
1290   },
1291
1292   {
1293     -1,                                 -1,
1294     -1,                                 -1,
1295     NULL,                               -1
1296   }
1297 };
1298
1299 static struct
1300 {
1301   int filetype;
1302   char *id;
1303 }
1304 filetype_id_list[] =
1305 {
1306   { LEVEL_FILE_TYPE_RND,        "RND"   },
1307   { LEVEL_FILE_TYPE_BD,         "BD"    },
1308   { LEVEL_FILE_TYPE_EM,         "EM"    },
1309   { LEVEL_FILE_TYPE_SP,         "SP"    },
1310   { LEVEL_FILE_TYPE_DX,         "DX"    },
1311   { LEVEL_FILE_TYPE_SB,         "SB"    },
1312   { LEVEL_FILE_TYPE_DC,         "DC"    },
1313   { -1,                         NULL    },
1314 };
1315
1316
1317 /* ========================================================================= */
1318 /* level file functions                                                      */
1319 /* ========================================================================= */
1320
1321 static boolean check_special_flags(char *flag)
1322 {
1323 #if 0
1324   printf("::: '%s', '%s', '%s'\n",
1325          flag,
1326          options.special_flags,
1327          leveldir_current->special_flags);
1328 #endif
1329
1330   if (strEqual(options.special_flags, flag) ||
1331       strEqual(leveldir_current->special_flags, flag))
1332     return TRUE;
1333
1334   return FALSE;
1335 }
1336
1337 static struct DateInfo getCurrentDate()
1338 {
1339   time_t epoch_seconds = time(NULL);
1340   struct tm *now = localtime(&epoch_seconds);
1341   struct DateInfo date;
1342
1343   date.year  = now->tm_year + 1900;
1344   date.month = now->tm_mon  + 1;
1345   date.day   = now->tm_mday;
1346
1347   date.src   = DATE_SRC_CLOCK;
1348
1349   return date;
1350 }
1351
1352 static void resetEventFlags(struct ElementChangeInfo *change)
1353 {
1354   int i;
1355
1356   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1357     change->has_event[i] = FALSE;
1358 }
1359
1360 static void resetEventBits()
1361 {
1362   int i;
1363
1364   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1365     xx_event_bits[i] = 0;
1366 }
1367
1368 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1369 {
1370   int i;
1371
1372   /* important: only change event flag if corresponding event bit is set
1373      (this is because all xx_event_bits[] values are loaded separately,
1374      and all xx_event_bits[] values are set back to zero before loading
1375      another value xx_event_bits[x] (each value representing 32 flags)) */
1376
1377   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1378     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1379       change->has_event[i] = TRUE;
1380 }
1381
1382 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1383 {
1384   int i;
1385
1386   /* in contrast to the above function setEventFlagsFromEventBits(), it
1387      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1388      depending on the corresponding change->has_event[i] values here, as
1389      all xx_event_bits[] values are reset in resetEventBits() before */
1390
1391   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1392     if (change->has_event[i])
1393       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1394 }
1395
1396 static char *getDefaultElementDescription(struct ElementInfo *ei)
1397 {
1398   static char description[MAX_ELEMENT_NAME_LEN + 1];
1399   char *default_description = (ei->custom_description != NULL ?
1400                                ei->custom_description :
1401                                ei->editor_description);
1402   int i;
1403
1404   /* always start with reliable default values */
1405   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1406     description[i] = '\0';
1407
1408   /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1409   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1410
1411   return &description[0];
1412 }
1413
1414 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1415 {
1416   char *default_description = getDefaultElementDescription(ei);
1417   int i;
1418
1419   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1420     ei->description[i] = default_description[i];
1421 }
1422
1423 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1424 {
1425   int i;
1426
1427   for (i = 0; conf[i].data_type != -1; i++)
1428   {
1429     int default_value = conf[i].default_value;
1430     int data_type = conf[i].data_type;
1431     int conf_type = conf[i].conf_type;
1432     int byte_mask = conf_type & CONF_MASK_BYTES;
1433
1434     if (byte_mask == CONF_MASK_MULTI_BYTES)
1435     {
1436       int default_num_entities = conf[i].default_num_entities;
1437       int max_num_entities = conf[i].max_num_entities;
1438
1439       *(int *)(conf[i].num_entities) = default_num_entities;
1440
1441       if (data_type == TYPE_STRING)
1442       {
1443         char *default_string = conf[i].default_string;
1444         char *string = (char *)(conf[i].value);
1445
1446         strncpy(string, default_string, max_num_entities);
1447       }
1448       else if (data_type == TYPE_ELEMENT_LIST)
1449       {
1450         int *element_array = (int *)(conf[i].value);
1451         int j;
1452
1453         for (j = 0; j < max_num_entities; j++)
1454           element_array[j] = default_value;
1455       }
1456       else if (data_type == TYPE_CONTENT_LIST)
1457       {
1458         struct Content *content = (struct Content *)(conf[i].value);
1459         int c, x, y;
1460
1461         for (c = 0; c < max_num_entities; c++)
1462           for (y = 0; y < 3; y++)
1463             for (x = 0; x < 3; x++)
1464               content[c].e[x][y] = default_value;
1465       }
1466     }
1467     else        /* constant size configuration data (1, 2 or 4 bytes) */
1468     {
1469       if (data_type == TYPE_BOOLEAN)
1470         *(boolean *)(conf[i].value) = default_value;
1471       else
1472         *(int *)    (conf[i].value) = default_value;
1473     }
1474   }
1475 }
1476
1477 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1478 {
1479   int i;
1480
1481   for (i = 0; conf[i].data_type != -1; i++)
1482   {
1483     int data_type = conf[i].data_type;
1484     int conf_type = conf[i].conf_type;
1485     int byte_mask = conf_type & CONF_MASK_BYTES;
1486
1487     if (byte_mask == CONF_MASK_MULTI_BYTES)
1488     {
1489       int max_num_entities = conf[i].max_num_entities;
1490
1491       if (data_type == TYPE_STRING)
1492       {
1493         char *string      = (char *)(conf[i].value);
1494         char *string_copy = (char *)(conf[i].value_copy);
1495
1496         strncpy(string_copy, string, max_num_entities);
1497       }
1498       else if (data_type == TYPE_ELEMENT_LIST)
1499       {
1500         int *element_array      = (int *)(conf[i].value);
1501         int *element_array_copy = (int *)(conf[i].value_copy);
1502         int j;
1503
1504         for (j = 0; j < max_num_entities; j++)
1505           element_array_copy[j] = element_array[j];
1506       }
1507       else if (data_type == TYPE_CONTENT_LIST)
1508       {
1509         struct Content *content      = (struct Content *)(conf[i].value);
1510         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1511         int c, x, y;
1512
1513         for (c = 0; c < max_num_entities; c++)
1514           for (y = 0; y < 3; y++)
1515             for (x = 0; x < 3; x++)
1516               content_copy[c].e[x][y] = content[c].e[x][y];
1517       }
1518     }
1519     else        /* constant size configuration data (1, 2 or 4 bytes) */
1520     {
1521       if (data_type == TYPE_BOOLEAN)
1522         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1523       else
1524         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
1525     }
1526   }
1527 }
1528
1529 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1530 {
1531   int i;
1532
1533   xx_ei = *ei_from;     /* copy element data into temporary buffer */
1534   yy_ei = *ei_to;       /* copy element data into temporary buffer */
1535
1536   copyConfigFromConfigList(chunk_config_CUSX_base);
1537
1538   *ei_from = xx_ei;
1539   *ei_to   = yy_ei;
1540
1541   /* ---------- reinitialize and copy change pages ---------- */
1542
1543   ei_to->num_change_pages = ei_from->num_change_pages;
1544   ei_to->current_change_page = ei_from->current_change_page;
1545
1546   setElementChangePages(ei_to, ei_to->num_change_pages);
1547
1548   for (i = 0; i < ei_to->num_change_pages; i++)
1549     ei_to->change_page[i] = ei_from->change_page[i];
1550
1551   /* ---------- copy group element info ---------- */
1552   if (ei_from->group != NULL && ei_to->group != NULL)   /* group or internal */
1553     *ei_to->group = *ei_from->group;
1554
1555   /* mark this custom element as modified */
1556   ei_to->modified_settings = TRUE;
1557 }
1558
1559 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1560 {
1561   int change_page_size = sizeof(struct ElementChangeInfo);
1562
1563   ei->num_change_pages = MAX(1, change_pages);
1564
1565   ei->change_page =
1566     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1567
1568   if (ei->current_change_page >= ei->num_change_pages)
1569     ei->current_change_page = ei->num_change_pages - 1;
1570
1571   ei->change = &ei->change_page[ei->current_change_page];
1572 }
1573
1574 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1575 {
1576   xx_change = *change;          /* copy change data into temporary buffer */
1577
1578 #if 0
1579   /* (not needed; set by setConfigToDefaultsFromConfigList()) */
1580   xx_num_contents = 1;
1581 #endif
1582
1583   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1584
1585   *change = xx_change;
1586
1587   resetEventFlags(change);
1588
1589   change->direct_action = 0;
1590   change->other_action = 0;
1591
1592   change->pre_change_function = NULL;
1593   change->change_function = NULL;
1594   change->post_change_function = NULL;
1595 }
1596
1597 #if 1
1598
1599 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1600 {
1601   int i, x, y;
1602
1603   li = *level;          /* copy level data into temporary buffer */
1604   setConfigToDefaultsFromConfigList(chunk_config_INFO);
1605   *level = li;          /* copy temporary buffer back to level data */
1606
1607   setLevelInfoToDefaults_EM();
1608   setLevelInfoToDefaults_SP();
1609
1610   level->native_em_level = &native_em_level;
1611   level->native_sp_level = &native_sp_level;
1612
1613   level->file_version = FILE_VERSION_ACTUAL;
1614   level->game_version = GAME_VERSION_ACTUAL;
1615
1616   level->creation_date = getCurrentDate();
1617
1618   level->encoding_16bit_field  = TRUE;
1619   level->encoding_16bit_yamyam = TRUE;
1620   level->encoding_16bit_amoeba = TRUE;
1621
1622   for (x = 0; x < MAX_LEV_FIELDX; x++)
1623     for (y = 0; y < MAX_LEV_FIELDY; y++)
1624       level->field[x][y] = EL_SAND;
1625
1626   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1627     level->name[i] = '\0';
1628   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1629     level->author[i] = '\0';
1630
1631   strcpy(level->name, NAMELESS_LEVEL_NAME);
1632   strcpy(level->author, ANONYMOUS_NAME);
1633
1634   level->field[0][0] = EL_PLAYER_1;
1635   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1636
1637   BorderElement = EL_STEELWALL;
1638
1639   /* set all bug compatibility flags to "false" => do not emulate this bug */
1640   level->use_action_after_change_bug = FALSE;
1641
1642   if (leveldir_current)
1643   {
1644     /* try to determine better author name than 'anonymous' */
1645     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1646     {
1647       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1648       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1649     }
1650     else
1651     {
1652       switch (LEVELCLASS(leveldir_current))
1653       {
1654         case LEVELCLASS_TUTORIAL:
1655           strcpy(level->author, PROGRAM_AUTHOR_STRING);
1656           break;
1657
1658         case LEVELCLASS_CONTRIB:
1659           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1660           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1661           break;
1662
1663         case LEVELCLASS_PRIVATE:
1664           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1665           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1666           break;
1667
1668         default:
1669           /* keep default value */
1670           break;
1671       }
1672     }
1673   }
1674 }
1675
1676 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1677 {
1678   static boolean clipboard_elements_initialized = FALSE;
1679   int i;
1680
1681   InitElementPropertiesStatic();
1682
1683   li = *level;          /* copy level data into temporary buffer */
1684   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1685   *level = li;          /* copy temporary buffer back to level data */
1686
1687   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1688   {
1689     int element = i;
1690     struct ElementInfo *ei = &element_info[element];
1691
1692     /* never initialize clipboard elements after the very first time */
1693     /* (to be able to use clipboard elements between several levels) */
1694     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1695       continue;
1696
1697     if (IS_ENVELOPE(element))
1698     {
1699       int envelope_nr = element - EL_ENVELOPE_1;
1700
1701       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1702
1703       level->envelope[envelope_nr] = xx_envelope;
1704     }
1705
1706     if (IS_CUSTOM_ELEMENT(element) ||
1707         IS_GROUP_ELEMENT(element) ||
1708         IS_INTERNAL_ELEMENT(element))
1709     {
1710       xx_ei = *ei;      /* copy element data into temporary buffer */
1711
1712       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1713
1714       *ei = xx_ei;
1715     }
1716
1717     setElementChangePages(ei, 1);
1718     setElementChangeInfoToDefaults(ei->change);
1719
1720     if (IS_CUSTOM_ELEMENT(element) ||
1721         IS_GROUP_ELEMENT(element) ||
1722         IS_INTERNAL_ELEMENT(element))
1723     {
1724       setElementDescriptionToDefault(ei);
1725
1726       ei->modified_settings = FALSE;
1727     }
1728
1729     if (IS_CUSTOM_ELEMENT(element) ||
1730         IS_INTERNAL_ELEMENT(element))
1731     {
1732       /* internal values used in level editor */
1733
1734       ei->access_type = 0;
1735       ei->access_layer = 0;
1736       ei->access_protected = 0;
1737       ei->walk_to_action = 0;
1738       ei->smash_targets = 0;
1739       ei->deadliness = 0;
1740
1741       ei->can_explode_by_fire = FALSE;
1742       ei->can_explode_smashed = FALSE;
1743       ei->can_explode_impact = FALSE;
1744
1745       ei->current_change_page = 0;
1746     }
1747
1748     if (IS_GROUP_ELEMENT(element) ||
1749         IS_INTERNAL_ELEMENT(element))
1750     {
1751       struct ElementGroupInfo *group;
1752
1753       /* initialize memory for list of elements in group */
1754       if (ei->group == NULL)
1755         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1756
1757       group = ei->group;
1758
1759       xx_group = *group;        /* copy group data into temporary buffer */
1760
1761       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1762
1763       *group = xx_group;
1764     }
1765   }
1766
1767   clipboard_elements_initialized = TRUE;
1768 }
1769
1770 static void setLevelInfoToDefaults(struct LevelInfo *level,
1771                                    boolean level_info_only)
1772 {
1773   setLevelInfoToDefaults_Level(level);
1774
1775   if (!level_info_only)
1776     setLevelInfoToDefaults_Elements(level);
1777
1778   level->no_valid_file = FALSE;
1779
1780   level->changed = FALSE;
1781 }
1782
1783 #else
1784
1785 static void setLevelInfoToDefaults(struct LevelInfo *level,
1786                                    boolean level_info_only)
1787 {
1788   static boolean clipboard_elements_initialized = FALSE;
1789   int i, x, y;
1790
1791   if (level_info_only)
1792     return;
1793
1794   InitElementPropertiesStatic();
1795
1796   li = *level;          /* copy level data into temporary buffer */
1797
1798   setConfigToDefaultsFromConfigList(chunk_config_INFO);
1799   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1800
1801   *level = li;          /* copy temporary buffer back to level data */
1802
1803   setLevelInfoToDefaults_EM();
1804   setLevelInfoToDefaults_SP();
1805
1806   level->native_em_level = &native_em_level;
1807   level->native_sp_level = &native_sp_level;
1808
1809   level->file_version = FILE_VERSION_ACTUAL;
1810   level->game_version = GAME_VERSION_ACTUAL;
1811
1812   level->creation_date = getCurrentDate();
1813
1814   level->encoding_16bit_field  = TRUE;
1815   level->encoding_16bit_yamyam = TRUE;
1816   level->encoding_16bit_amoeba = TRUE;
1817
1818   for (x = 0; x < MAX_LEV_FIELDX; x++)
1819     for (y = 0; y < MAX_LEV_FIELDY; y++)
1820       level->field[x][y] = EL_SAND;
1821
1822   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1823     level->name[i] = '\0';
1824   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1825     level->author[i] = '\0';
1826
1827   strcpy(level->name, NAMELESS_LEVEL_NAME);
1828   strcpy(level->author, ANONYMOUS_NAME);
1829
1830   level->field[0][0] = EL_PLAYER_1;
1831   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1832
1833   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1834   {
1835     int element = i;
1836     struct ElementInfo *ei = &element_info[element];
1837
1838     /* never initialize clipboard elements after the very first time */
1839     /* (to be able to use clipboard elements between several levels) */
1840     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1841       continue;
1842
1843     if (IS_ENVELOPE(element))
1844     {
1845       int envelope_nr = element - EL_ENVELOPE_1;
1846
1847       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1848
1849       level->envelope[envelope_nr] = xx_envelope;
1850     }
1851
1852     if (IS_CUSTOM_ELEMENT(element) ||
1853         IS_GROUP_ELEMENT(element) ||
1854         IS_INTERNAL_ELEMENT(element))
1855     {
1856       xx_ei = *ei;      /* copy element data into temporary buffer */
1857
1858       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1859
1860       *ei = xx_ei;
1861     }
1862
1863     setElementChangePages(ei, 1);
1864     setElementChangeInfoToDefaults(ei->change);
1865
1866     if (IS_CUSTOM_ELEMENT(element) ||
1867         IS_GROUP_ELEMENT(element) ||
1868         IS_INTERNAL_ELEMENT(element))
1869     {
1870       setElementDescriptionToDefault(ei);
1871
1872       ei->modified_settings = FALSE;
1873     }
1874
1875     if (IS_CUSTOM_ELEMENT(element) ||
1876         IS_INTERNAL_ELEMENT(element))
1877     {
1878       /* internal values used in level editor */
1879
1880       ei->access_type = 0;
1881       ei->access_layer = 0;
1882       ei->access_protected = 0;
1883       ei->walk_to_action = 0;
1884       ei->smash_targets = 0;
1885       ei->deadliness = 0;
1886
1887       ei->can_explode_by_fire = FALSE;
1888       ei->can_explode_smashed = FALSE;
1889       ei->can_explode_impact = FALSE;
1890
1891       ei->current_change_page = 0;
1892     }
1893
1894     if (IS_GROUP_ELEMENT(element) ||
1895         IS_INTERNAL_ELEMENT(element))
1896     {
1897       struct ElementGroupInfo *group;
1898
1899       /* initialize memory for list of elements in group */
1900       if (ei->group == NULL)
1901         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1902
1903       group = ei->group;
1904
1905       xx_group = *group;        /* copy group data into temporary buffer */
1906
1907       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1908
1909       *group = xx_group;
1910     }
1911   }
1912
1913   clipboard_elements_initialized = TRUE;
1914
1915   BorderElement = EL_STEELWALL;
1916
1917   level->no_valid_file = FALSE;
1918
1919   level->changed = FALSE;
1920
1921   /* set all bug compatibility flags to "false" => do not emulate this bug */
1922   level->use_action_after_change_bug = FALSE;
1923
1924   if (leveldir_current)
1925   {
1926     /* try to determine better author name than 'anonymous' */
1927     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1928     {
1929       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1930       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1931     }
1932     else
1933     {
1934       switch (LEVELCLASS(leveldir_current))
1935       {
1936         case LEVELCLASS_TUTORIAL:
1937           strcpy(level->author, PROGRAM_AUTHOR_STRING);
1938           break;
1939
1940         case LEVELCLASS_CONTRIB:
1941           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1942           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1943           break;
1944
1945         case LEVELCLASS_PRIVATE:
1946           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1947           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1948           break;
1949
1950         default:
1951           /* keep default value */
1952           break;
1953       }
1954     }
1955   }
1956 }
1957
1958 #endif
1959
1960 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1961 {
1962   level_file_info->nr = 0;
1963   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1964   level_file_info->packed = FALSE;
1965   level_file_info->basename = NULL;
1966   level_file_info->filename = NULL;
1967 }
1968
1969 static void ActivateLevelTemplate()
1970 {
1971   int x, y;
1972
1973   /* Currently there is no special action needed to activate the template
1974      data, because 'element_info' property settings overwrite the original
1975      level data, while all other variables do not change. */
1976
1977   /* Exception: 'from_level_template' elements in the original level playfield
1978      are overwritten with the corresponding elements at the same position in
1979      playfield from the level template. */
1980
1981   for (x = 0; x < level.fieldx; x++)
1982     for (y = 0; y < level.fieldy; y++)
1983       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1984         level.field[x][y] = level_template.field[x][y];
1985
1986   if (check_special_flags("load_xsb_to_ces"))
1987   {
1988     struct LevelInfo level_backup = level;
1989
1990     /* overwrite all individual level settings from template level settings */
1991     level = level_template;
1992
1993     /* restore playfield size */
1994     level.fieldx = level_backup.fieldx;
1995     level.fieldy = level_backup.fieldy;
1996
1997     /* restore playfield content */
1998     for (x = 0; x < level.fieldx; x++)
1999       for (y = 0; y < level.fieldy; y++)
2000         level.field[x][y] = level_backup.field[x][y];
2001
2002     /* restore name and author from individual level */
2003     strcpy(level.name,   level_backup.name);
2004     strcpy(level.author, level_backup.author);
2005
2006     /* restore flag "use_custom_template" */
2007     level.use_custom_template = level_backup.use_custom_template;
2008   }
2009 }
2010
2011 static char *getLevelFilenameFromBasename(char *basename)
2012 {
2013   static char *filename = NULL;
2014
2015   checked_free(filename);
2016
2017   filename = getPath2(getCurrentLevelDir(), basename);
2018
2019   return filename;
2020 }
2021
2022 static int getFileTypeFromBasename(char *basename)
2023 {
2024   /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
2025
2026   static char *filename = NULL;
2027   struct stat file_status;
2028
2029   /* ---------- try to determine file type from filename ---------- */
2030
2031   /* check for typical filename of a Supaplex level package file */
2032 #if 1
2033   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2034     return LEVEL_FILE_TYPE_SP;
2035 #else
2036   if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
2037                                  strncmp(basename, "LEVELS.D", 8) == 0))
2038     return LEVEL_FILE_TYPE_SP;
2039 #endif
2040
2041   /* check for typical filename of a Diamond Caves II level package file */
2042   if (strSuffixLower(basename, ".dc") ||
2043       strSuffixLower(basename, ".dc2"))
2044     return LEVEL_FILE_TYPE_DC;
2045
2046   /* check for typical filename of a Sokoban level package file */
2047   if (strSuffixLower(basename, ".xsb") &&
2048       strchr(basename, '%') == NULL)
2049     return LEVEL_FILE_TYPE_SB;
2050
2051   /* ---------- try to determine file type from filesize ---------- */
2052
2053   checked_free(filename);
2054   filename = getPath2(getCurrentLevelDir(), basename);
2055
2056   if (stat(filename, &file_status) == 0)
2057   {
2058     /* check for typical filesize of a Supaplex level package file */
2059     if (file_status.st_size == 170496)
2060       return LEVEL_FILE_TYPE_SP;
2061   }
2062
2063   return LEVEL_FILE_TYPE_UNKNOWN;
2064 }
2065
2066 static boolean checkForPackageFromBasename(char *basename)
2067 {
2068   /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2069      !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!! */
2070
2071   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2072 }
2073
2074 static char *getSingleLevelBasenameExt(int nr, char *extension)
2075 {
2076   static char basename[MAX_FILENAME_LEN];
2077
2078   if (nr < 0)
2079     sprintf(basename, "template.%s", extension);
2080   else
2081     sprintf(basename, "%03d.%s", nr, extension);
2082
2083   return basename;
2084 }
2085
2086 static char *getSingleLevelBasename(int nr)
2087 {
2088   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2089 }
2090
2091 #if 1
2092
2093 static char *getPackedLevelBasename(int type)
2094 {
2095   static char basename[MAX_FILENAME_LEN];
2096   char *directory = getCurrentLevelDir();
2097   Directory *dir;
2098   DirectoryEntry *dir_entry;
2099
2100   strcpy(basename, UNDEFINED_FILENAME);         /* default: undefined file */
2101
2102   if ((dir = openDirectory(directory)) == NULL)
2103   {
2104     Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2105
2106     return basename;
2107   }
2108
2109   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
2110   {
2111     char *entry_basename = dir_entry->basename;
2112     int entry_type = getFileTypeFromBasename(entry_basename);
2113
2114     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  /* found valid level package */
2115     {
2116       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2117           type == entry_type)
2118       {
2119         strcpy(basename, entry_basename);
2120
2121         break;
2122       }
2123     }
2124   }
2125
2126   closeDirectory(dir);
2127
2128   return basename;
2129 }
2130
2131 #else
2132
2133 static char *getPackedLevelBasename(int type)
2134 {
2135   static char basename[MAX_FILENAME_LEN];
2136   char *directory = getCurrentLevelDir();
2137   DIR *dir;
2138   struct dirent *dir_entry;
2139
2140   strcpy(basename, UNDEFINED_FILENAME);         /* default: undefined file */
2141
2142   if ((dir = opendir(directory)) == NULL)
2143   {
2144     Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2145
2146     return basename;
2147   }
2148
2149   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
2150   {
2151     char *entry_basename = dir_entry->d_name;
2152     int entry_type = getFileTypeFromBasename(entry_basename);
2153
2154     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  /* found valid level package */
2155     {
2156       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2157           type == entry_type)
2158       {
2159         strcpy(basename, entry_basename);
2160
2161         break;
2162       }
2163     }
2164   }
2165
2166   closedir(dir);
2167
2168   return basename;
2169 }
2170
2171 #endif
2172
2173 static char *getSingleLevelFilename(int nr)
2174 {
2175   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2176 }
2177
2178 #if 0
2179 static char *getPackedLevelFilename(int type)
2180 {
2181   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2182 }
2183 #endif
2184
2185 char *getDefaultLevelFilename(int nr)
2186 {
2187   return getSingleLevelFilename(nr);
2188 }
2189
2190 #if 0
2191 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2192                                                  int type)
2193 {
2194   lfi->type = type;
2195   lfi->packed = FALSE;
2196   lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2197   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2198 }
2199 #endif
2200
2201 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2202                                                  int type, char *format, ...)
2203 {
2204   static char basename[MAX_FILENAME_LEN];
2205   va_list ap;
2206
2207   va_start(ap, format);
2208   vsprintf(basename, format, ap);
2209   va_end(ap);
2210
2211   lfi->type = type;
2212   lfi->packed = FALSE;
2213   lfi->basename = basename;
2214   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2215 }
2216
2217 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2218                                                  int type)
2219 {
2220   lfi->type = type;
2221   lfi->packed = TRUE;
2222   lfi->basename = getPackedLevelBasename(lfi->type);
2223   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2224 }
2225
2226 static int getFiletypeFromID(char *filetype_id)
2227 {
2228   char *filetype_id_lower;
2229   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2230   int i;
2231
2232   if (filetype_id == NULL)
2233     return LEVEL_FILE_TYPE_UNKNOWN;
2234
2235   filetype_id_lower = getStringToLower(filetype_id);
2236
2237   for (i = 0; filetype_id_list[i].id != NULL; i++)
2238   {
2239     char *id_lower = getStringToLower(filetype_id_list[i].id);
2240     
2241     if (strEqual(filetype_id_lower, id_lower))
2242       filetype = filetype_id_list[i].filetype;
2243
2244     free(id_lower);
2245
2246     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2247       break;
2248   }
2249
2250   free(filetype_id_lower);
2251
2252   return filetype;
2253 }
2254
2255 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2256 {
2257   int nr = lfi->nr;
2258
2259   /* special case: level number is negative => check for level template file */
2260   if (nr < 0)
2261   {
2262 #if 1
2263     /* global variable "leveldir_current" must be modified in the loop below */
2264     LevelDirTree *leveldir_current_last = leveldir_current;
2265
2266     /* check for template level in path from current to topmost tree node */
2267
2268     while (leveldir_current != NULL)
2269     {
2270       setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2271                                            "template.%s", LEVELFILE_EXTENSION);
2272
2273       if (fileExists(lfi->filename))
2274         break;
2275
2276       leveldir_current = leveldir_current->node_parent;
2277     }
2278
2279     /* restore global variable "leveldir_current" modified in above loop */
2280     leveldir_current = leveldir_current_last;
2281
2282 #else
2283
2284     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2285                                          "template.%s", LEVELFILE_EXTENSION);
2286
2287 #endif
2288
2289     /* no fallback if template file not existing */
2290     return;
2291   }
2292
2293   /* special case: check for file name/pattern specified in "levelinfo.conf" */
2294   if (leveldir_current->level_filename != NULL)
2295   {
2296     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2297
2298     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2299                                          leveldir_current->level_filename, nr);
2300
2301     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2302
2303     if (fileExists(lfi->filename))
2304       return;
2305   }
2306
2307   /* check for native Rocks'n'Diamonds level file */
2308   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2309                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2310   if (fileExists(lfi->filename))
2311     return;
2312
2313   /* check for Emerald Mine level file (V1) */
2314   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2315                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2316   if (fileExists(lfi->filename))
2317     return;
2318   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2319                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2320   if (fileExists(lfi->filename))
2321     return;
2322
2323   /* check for Emerald Mine level file (V2 to V5) */
2324   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2325   if (fileExists(lfi->filename))
2326     return;
2327
2328   /* check for Emerald Mine level file (V6 / single mode) */
2329   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2330   if (fileExists(lfi->filename))
2331     return;
2332   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2333   if (fileExists(lfi->filename))
2334     return;
2335
2336   /* check for Emerald Mine level file (V6 / teamwork mode) */
2337   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2338   if (fileExists(lfi->filename))
2339     return;
2340   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2341   if (fileExists(lfi->filename))
2342     return;
2343
2344   /* check for various packed level file formats */
2345   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2346   if (fileExists(lfi->filename))
2347     return;
2348
2349   /* no known level file found -- use default values (and fail later) */
2350   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2351                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2352 }
2353
2354 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2355 {
2356   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2357     lfi->type = getFileTypeFromBasename(lfi->basename);
2358 }
2359
2360 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2361 {
2362   /* always start with reliable default values */
2363   setFileInfoToDefaults(level_file_info);
2364
2365   level_file_info->nr = nr;     /* set requested level number */
2366
2367   determineLevelFileInfo_Filename(level_file_info);
2368   determineLevelFileInfo_Filetype(level_file_info);
2369 }
2370
2371 /* ------------------------------------------------------------------------- */
2372 /* functions for loading R'n'D level                                         */
2373 /* ------------------------------------------------------------------------- */
2374
2375 int getMappedElement(int element)
2376 {
2377   /* remap some (historic, now obsolete) elements */
2378
2379   switch (element)
2380   {
2381     case EL_PLAYER_OBSOLETE:
2382       element = EL_PLAYER_1;
2383       break;
2384
2385     case EL_KEY_OBSOLETE:
2386       element = EL_KEY_1;
2387       break;
2388
2389     case EL_EM_KEY_1_FILE_OBSOLETE:
2390       element = EL_EM_KEY_1;
2391       break;
2392
2393     case EL_EM_KEY_2_FILE_OBSOLETE:
2394       element = EL_EM_KEY_2;
2395       break;
2396
2397     case EL_EM_KEY_3_FILE_OBSOLETE:
2398       element = EL_EM_KEY_3;
2399       break;
2400
2401     case EL_EM_KEY_4_FILE_OBSOLETE:
2402       element = EL_EM_KEY_4;
2403       break;
2404
2405     case EL_ENVELOPE_OBSOLETE:
2406       element = EL_ENVELOPE_1;
2407       break;
2408
2409     case EL_SP_EMPTY:
2410       element = EL_EMPTY;
2411       break;
2412
2413     default:
2414       if (element >= NUM_FILE_ELEMENTS)
2415       {
2416         Error(ERR_WARN, "invalid level element %d", element);
2417
2418         element = EL_UNKNOWN;
2419       }
2420       break;
2421   }
2422
2423   return element;
2424 }
2425
2426 int getMappedElementByVersion(int element, int game_version)
2427 {
2428   /* remap some elements due to certain game version */
2429
2430   if (game_version <= VERSION_IDENT(2,2,0,0))
2431   {
2432     /* map game font elements */
2433     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2434                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2435                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2436                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2437   }
2438
2439   if (game_version < VERSION_IDENT(3,0,0,0))
2440   {
2441     /* map Supaplex gravity tube elements */
2442     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2443                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2444                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2445                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2446                element);
2447   }
2448
2449   return element;
2450 }
2451
2452 #if 1
2453
2454 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2455 {
2456   level->file_version = getFileVersion(file);
2457   level->game_version = getFileVersion(file);
2458
2459   return chunk_size;
2460 }
2461
2462 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2463 {
2464   level->creation_date.year  = getFile16BitBE(file);
2465   level->creation_date.month = getFile8Bit(file);
2466   level->creation_date.day   = getFile8Bit(file);
2467
2468   level->creation_date.src   = DATE_SRC_LEVELFILE;
2469
2470   return chunk_size;
2471 }
2472
2473 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2474 {
2475   int initial_player_stepsize;
2476   int initial_player_gravity;
2477   int i, x, y;
2478
2479   level->fieldx = getFile8Bit(file);
2480   level->fieldy = getFile8Bit(file);
2481
2482   level->time           = getFile16BitBE(file);
2483   level->gems_needed    = getFile16BitBE(file);
2484
2485   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2486     level->name[i] = getFile8Bit(file);
2487   level->name[MAX_LEVEL_NAME_LEN] = 0;
2488
2489   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2490     level->score[i] = getFile8Bit(file);
2491
2492   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2493   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2494     for (y = 0; y < 3; y++)
2495       for (x = 0; x < 3; x++)
2496         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2497
2498   level->amoeba_speed           = getFile8Bit(file);
2499   level->time_magic_wall        = getFile8Bit(file);
2500   level->time_wheel             = getFile8Bit(file);
2501   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2502
2503   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2504                                    STEPSIZE_NORMAL);
2505
2506   for (i = 0; i < MAX_PLAYERS; i++)
2507     level->initial_player_stepsize[i] = initial_player_stepsize;
2508
2509   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2510
2511   for (i = 0; i < MAX_PLAYERS; i++)
2512     level->initial_player_gravity[i] = initial_player_gravity;
2513
2514   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2515   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2516
2517   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2518
2519   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2520   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2521   level->can_move_into_acid_bits = getFile32BitBE(file);
2522   level->dont_collide_with_bits = getFile8Bit(file);
2523
2524   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2525   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2526
2527   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2528   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2529   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2530
2531   level->game_engine_type       = getFile8Bit(file);
2532
2533   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2534
2535   return chunk_size;
2536 }
2537
2538 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2539 {
2540   int i;
2541
2542   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2543     level->name[i] = getFile8Bit(file);
2544   level->name[MAX_LEVEL_NAME_LEN] = 0;
2545
2546   return chunk_size;
2547 }
2548
2549 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2550 {
2551   int i;
2552
2553   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2554     level->author[i] = getFile8Bit(file);
2555   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2556
2557   return chunk_size;
2558 }
2559
2560 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2561 {
2562   int x, y;
2563   int chunk_size_expected = level->fieldx * level->fieldy;
2564
2565   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2566      stored with 16-bit encoding (and should be twice as big then).
2567      Even worse, playfield data was stored 16-bit when only yamyam content
2568      contained 16-bit elements and vice versa. */
2569
2570   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2571     chunk_size_expected *= 2;
2572
2573   if (chunk_size_expected != chunk_size)
2574   {
2575     ReadUnusedBytesFromFile(file, chunk_size);
2576     return chunk_size_expected;
2577   }
2578
2579   for (y = 0; y < level->fieldy; y++)
2580     for (x = 0; x < level->fieldx; x++)
2581       level->field[x][y] =
2582         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2583                          getFile8Bit(file));
2584   return chunk_size;
2585 }
2586
2587 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2588 {
2589   int i, x, y;
2590   int header_size = 4;
2591   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2592   int chunk_size_expected = header_size + content_size;
2593
2594   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2595      stored with 16-bit encoding (and should be twice as big then).
2596      Even worse, playfield data was stored 16-bit when only yamyam content
2597      contained 16-bit elements and vice versa. */
2598
2599   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2600     chunk_size_expected += content_size;
2601
2602   if (chunk_size_expected != chunk_size)
2603   {
2604     ReadUnusedBytesFromFile(file, chunk_size);
2605     return chunk_size_expected;
2606   }
2607
2608   getFile8Bit(file);
2609   level->num_yamyam_contents = getFile8Bit(file);
2610   getFile8Bit(file);
2611   getFile8Bit(file);
2612
2613   /* correct invalid number of content fields -- should never happen */
2614   if (level->num_yamyam_contents < 1 ||
2615       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2616     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2617
2618   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2619     for (y = 0; y < 3; y++)
2620       for (x = 0; x < 3; x++)
2621         level->yamyam_content[i].e[x][y] =
2622           getMappedElement(level->encoding_16bit_field ?
2623                            getFile16BitBE(file) : getFile8Bit(file));
2624   return chunk_size;
2625 }
2626
2627 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2628 {
2629   int i, x, y;
2630   int element;
2631   int num_contents;
2632 #if 0
2633   int content_xsize, content_ysize;
2634 #endif
2635   int content_array[MAX_ELEMENT_CONTENTS][3][3];
2636
2637   element = getMappedElement(getFile16BitBE(file));
2638   num_contents = getFile8Bit(file);
2639 #if 1
2640   getFile8Bit(file);    /* content x size (unused) */
2641   getFile8Bit(file);    /* content y size (unused) */
2642 #else
2643   content_xsize = getFile8Bit(file);
2644   content_ysize = getFile8Bit(file);
2645 #endif
2646
2647   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2648
2649   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2650     for (y = 0; y < 3; y++)
2651       for (x = 0; x < 3; x++)
2652         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2653
2654   /* correct invalid number of content fields -- should never happen */
2655   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2656     num_contents = STD_ELEMENT_CONTENTS;
2657
2658   if (element == EL_YAMYAM)
2659   {
2660     level->num_yamyam_contents = num_contents;
2661
2662     for (i = 0; i < num_contents; i++)
2663       for (y = 0; y < 3; y++)
2664         for (x = 0; x < 3; x++)
2665           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2666   }
2667   else if (element == EL_BD_AMOEBA)
2668   {
2669     level->amoeba_content = content_array[0][0][0];
2670   }
2671   else
2672   {
2673     Error(ERR_WARN, "cannot load content for element '%d'", element);
2674   }
2675
2676   return chunk_size;
2677 }
2678
2679 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2680 {
2681   int i;
2682   int element;
2683   int envelope_nr;
2684   int envelope_len;
2685   int chunk_size_expected;
2686
2687   element = getMappedElement(getFile16BitBE(file));
2688   if (!IS_ENVELOPE(element))
2689     element = EL_ENVELOPE_1;
2690
2691   envelope_nr = element - EL_ENVELOPE_1;
2692
2693   envelope_len = getFile16BitBE(file);
2694
2695   level->envelope[envelope_nr].xsize = getFile8Bit(file);
2696   level->envelope[envelope_nr].ysize = getFile8Bit(file);
2697
2698   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2699
2700   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2701   if (chunk_size_expected != chunk_size)
2702   {
2703     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2704     return chunk_size_expected;
2705   }
2706
2707   for (i = 0; i < envelope_len; i++)
2708     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2709
2710   return chunk_size;
2711 }
2712
2713 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2714 {
2715   int num_changed_custom_elements = getFile16BitBE(file);
2716   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2717   int i;
2718
2719   if (chunk_size_expected != chunk_size)
2720   {
2721     ReadUnusedBytesFromFile(file, chunk_size - 2);
2722     return chunk_size_expected;
2723   }
2724
2725   for (i = 0; i < num_changed_custom_elements; i++)
2726   {
2727     int element = getMappedElement(getFile16BitBE(file));
2728     int properties = getFile32BitBE(file);
2729
2730     if (IS_CUSTOM_ELEMENT(element))
2731       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2732     else
2733       Error(ERR_WARN, "invalid custom element number %d", element);
2734
2735     /* older game versions that wrote level files with CUS1 chunks used
2736        different default push delay values (not yet stored in level file) */
2737     element_info[element].push_delay_fixed = 2;
2738     element_info[element].push_delay_random = 8;
2739   }
2740
2741   return chunk_size;
2742 }
2743
2744 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2745 {
2746   int num_changed_custom_elements = getFile16BitBE(file);
2747   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2748   int i;
2749
2750   if (chunk_size_expected != chunk_size)
2751   {
2752     ReadUnusedBytesFromFile(file, chunk_size - 2);
2753     return chunk_size_expected;
2754   }
2755
2756   for (i = 0; i < num_changed_custom_elements; i++)
2757   {
2758     int element = getMappedElement(getFile16BitBE(file));
2759     int custom_target_element = getMappedElement(getFile16BitBE(file));
2760
2761     if (IS_CUSTOM_ELEMENT(element))
2762       element_info[element].change->target_element = custom_target_element;
2763     else
2764       Error(ERR_WARN, "invalid custom element number %d", element);
2765   }
2766
2767   return chunk_size;
2768 }
2769
2770 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2771 {
2772   int num_changed_custom_elements = getFile16BitBE(file);
2773   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2774   int i, j, x, y;
2775
2776   if (chunk_size_expected != chunk_size)
2777   {
2778     ReadUnusedBytesFromFile(file, chunk_size - 2);
2779     return chunk_size_expected;
2780   }
2781
2782   for (i = 0; i < num_changed_custom_elements; i++)
2783   {
2784     int element = getMappedElement(getFile16BitBE(file));
2785     struct ElementInfo *ei = &element_info[element];
2786     unsigned int event_bits;
2787
2788     if (!IS_CUSTOM_ELEMENT(element))
2789     {
2790       Error(ERR_WARN, "invalid custom element number %d", element);
2791
2792       element = EL_INTERNAL_DUMMY;
2793     }
2794
2795     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2796       ei->description[j] = getFile8Bit(file);
2797     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2798
2799     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2800
2801     /* some free bytes for future properties and padding */
2802     ReadUnusedBytesFromFile(file, 7);
2803
2804     ei->use_gfx_element = getFile8Bit(file);
2805     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2806
2807     ei->collect_score_initial = getFile8Bit(file);
2808     ei->collect_count_initial = getFile8Bit(file);
2809
2810     ei->push_delay_fixed = getFile16BitBE(file);
2811     ei->push_delay_random = getFile16BitBE(file);
2812     ei->move_delay_fixed = getFile16BitBE(file);
2813     ei->move_delay_random = getFile16BitBE(file);
2814
2815     ei->move_pattern = getFile16BitBE(file);
2816     ei->move_direction_initial = getFile8Bit(file);
2817     ei->move_stepsize = getFile8Bit(file);
2818
2819     for (y = 0; y < 3; y++)
2820       for (x = 0; x < 3; x++)
2821         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2822
2823     event_bits = getFile32BitBE(file);
2824     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2825       if (event_bits & (1 << j))
2826         ei->change->has_event[j] = TRUE;
2827
2828     ei->change->target_element = getMappedElement(getFile16BitBE(file));
2829
2830     ei->change->delay_fixed = getFile16BitBE(file);
2831     ei->change->delay_random = getFile16BitBE(file);
2832     ei->change->delay_frames = getFile16BitBE(file);
2833
2834     ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2835
2836     ei->change->explode = getFile8Bit(file);
2837     ei->change->use_target_content = getFile8Bit(file);
2838     ei->change->only_if_complete = getFile8Bit(file);
2839     ei->change->use_random_replace = getFile8Bit(file);
2840
2841     ei->change->random_percentage = getFile8Bit(file);
2842     ei->change->replace_when = getFile8Bit(file);
2843
2844     for (y = 0; y < 3; y++)
2845       for (x = 0; x < 3; x++)
2846         ei->change->target_content.e[x][y] =
2847           getMappedElement(getFile16BitBE(file));
2848
2849     ei->slippery_type = getFile8Bit(file);
2850
2851     /* some free bytes for future properties and padding */
2852     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2853
2854     /* mark that this custom element has been modified */
2855     ei->modified_settings = TRUE;
2856   }
2857
2858   return chunk_size;
2859 }
2860
2861 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2862 {
2863   struct ElementInfo *ei;
2864   int chunk_size_expected;
2865   int element;
2866   int i, j, x, y;
2867
2868   /* ---------- custom element base property values (96 bytes) ------------- */
2869
2870   element = getMappedElement(getFile16BitBE(file));
2871
2872   if (!IS_CUSTOM_ELEMENT(element))
2873   {
2874     Error(ERR_WARN, "invalid custom element number %d", element);
2875
2876     ReadUnusedBytesFromFile(file, chunk_size - 2);
2877     return chunk_size;
2878   }
2879
2880   ei = &element_info[element];
2881
2882   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2883     ei->description[i] = getFile8Bit(file);
2884   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2885
2886   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2887
2888   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
2889
2890   ei->num_change_pages = getFile8Bit(file);
2891
2892   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2893   if (chunk_size_expected != chunk_size)
2894   {
2895     ReadUnusedBytesFromFile(file, chunk_size - 43);
2896     return chunk_size_expected;
2897   }
2898
2899   ei->ce_value_fixed_initial = getFile16BitBE(file);
2900   ei->ce_value_random_initial = getFile16BitBE(file);
2901   ei->use_last_ce_value = getFile8Bit(file);
2902
2903   ei->use_gfx_element = getFile8Bit(file);
2904   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2905
2906   ei->collect_score_initial = getFile8Bit(file);
2907   ei->collect_count_initial = getFile8Bit(file);
2908
2909   ei->drop_delay_fixed = getFile8Bit(file);
2910   ei->push_delay_fixed = getFile8Bit(file);
2911   ei->drop_delay_random = getFile8Bit(file);
2912   ei->push_delay_random = getFile8Bit(file);
2913   ei->move_delay_fixed = getFile16BitBE(file);
2914   ei->move_delay_random = getFile16BitBE(file);
2915
2916   /* bits 0 - 15 of "move_pattern" ... */
2917   ei->move_pattern = getFile16BitBE(file);
2918   ei->move_direction_initial = getFile8Bit(file);
2919   ei->move_stepsize = getFile8Bit(file);
2920
2921   ei->slippery_type = getFile8Bit(file);
2922
2923   for (y = 0; y < 3; y++)
2924     for (x = 0; x < 3; x++)
2925       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2926
2927   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2928   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2929   ei->move_leave_type = getFile8Bit(file);
2930
2931   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2932   ei->move_pattern |= (getFile16BitBE(file) << 16);
2933
2934   ei->access_direction = getFile8Bit(file);
2935
2936   ei->explosion_delay = getFile8Bit(file);
2937   ei->ignition_delay = getFile8Bit(file);
2938   ei->explosion_type = getFile8Bit(file);
2939
2940   /* some free bytes for future custom property values and padding */
2941   ReadUnusedBytesFromFile(file, 1);
2942
2943   /* ---------- change page property values (48 bytes) --------------------- */
2944
2945   setElementChangePages(ei, ei->num_change_pages);
2946
2947   for (i = 0; i < ei->num_change_pages; i++)
2948   {
2949     struct ElementChangeInfo *change = &ei->change_page[i];
2950     unsigned int event_bits;
2951
2952     /* always start with reliable default values */
2953     setElementChangeInfoToDefaults(change);
2954
2955     /* bits 0 - 31 of "has_event[]" ... */
2956     event_bits = getFile32BitBE(file);
2957     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2958       if (event_bits & (1 << j))
2959         change->has_event[j] = TRUE;
2960
2961     change->target_element = getMappedElement(getFile16BitBE(file));
2962
2963     change->delay_fixed = getFile16BitBE(file);
2964     change->delay_random = getFile16BitBE(file);
2965     change->delay_frames = getFile16BitBE(file);
2966
2967     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2968
2969     change->explode = getFile8Bit(file);
2970     change->use_target_content = getFile8Bit(file);
2971     change->only_if_complete = getFile8Bit(file);
2972     change->use_random_replace = getFile8Bit(file);
2973
2974     change->random_percentage = getFile8Bit(file);
2975     change->replace_when = getFile8Bit(file);
2976
2977     for (y = 0; y < 3; y++)
2978       for (x = 0; x < 3; x++)
2979         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2980
2981     change->can_change = getFile8Bit(file);
2982
2983     change->trigger_side = getFile8Bit(file);
2984
2985     change->trigger_player = getFile8Bit(file);
2986     change->trigger_page = getFile8Bit(file);
2987
2988     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2989                             CH_PAGE_ANY : (1 << change->trigger_page));
2990
2991     change->has_action = getFile8Bit(file);
2992     change->action_type = getFile8Bit(file);
2993     change->action_mode = getFile8Bit(file);
2994     change->action_arg = getFile16BitBE(file);
2995
2996     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2997     event_bits = getFile8Bit(file);
2998     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2999       if (event_bits & (1 << (j - 32)))
3000         change->has_event[j] = TRUE;
3001   }
3002
3003   /* mark this custom element as modified */
3004   ei->modified_settings = TRUE;
3005
3006   return chunk_size;
3007 }
3008
3009 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3010 {
3011   struct ElementInfo *ei;
3012   struct ElementGroupInfo *group;
3013   int element;
3014   int i;
3015
3016   element = getMappedElement(getFile16BitBE(file));
3017
3018   if (!IS_GROUP_ELEMENT(element))
3019   {
3020     Error(ERR_WARN, "invalid group element number %d", element);
3021
3022     ReadUnusedBytesFromFile(file, chunk_size - 2);
3023     return chunk_size;
3024   }
3025
3026   ei = &element_info[element];
3027
3028   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3029     ei->description[i] = getFile8Bit(file);
3030   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3031
3032   group = element_info[element].group;
3033
3034   group->num_elements = getFile8Bit(file);
3035
3036   ei->use_gfx_element = getFile8Bit(file);
3037   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3038
3039   group->choice_mode = getFile8Bit(file);
3040
3041   /* some free bytes for future values and padding */
3042   ReadUnusedBytesFromFile(file, 3);
3043
3044   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3045     group->element[i] = getMappedElement(getFile16BitBE(file));
3046
3047   /* mark this group element as modified */
3048   element_info[element].modified_settings = TRUE;
3049
3050   return chunk_size;
3051 }
3052
3053 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3054                                 int element, int real_element)
3055 {
3056   int micro_chunk_size = 0;
3057   int conf_type = getFile8Bit(file);
3058   int byte_mask = conf_type & CONF_MASK_BYTES;
3059   boolean element_found = FALSE;
3060   int i;
3061
3062   micro_chunk_size += 1;
3063
3064   if (byte_mask == CONF_MASK_MULTI_BYTES)
3065   {
3066     int num_bytes = getFile16BitBE(file);
3067     byte *buffer = checked_malloc(num_bytes);
3068
3069     ReadBytesFromFile(file, buffer, num_bytes);
3070
3071     for (i = 0; conf[i].data_type != -1; i++)
3072     {
3073       if (conf[i].element == element &&
3074           conf[i].conf_type == conf_type)
3075       {
3076         int data_type = conf[i].data_type;
3077         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3078         int max_num_entities = conf[i].max_num_entities;
3079
3080         if (num_entities > max_num_entities)
3081         {
3082           Error(ERR_WARN,
3083                 "truncating number of entities for element %d from %d to %d",
3084                 element, num_entities, max_num_entities);
3085
3086           num_entities = max_num_entities;
3087         }
3088
3089         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3090                                   data_type == TYPE_CONTENT_LIST))
3091         {
3092           /* for element and content lists, zero entities are not allowed */
3093           Error(ERR_WARN, "found empty list of entities for element %d",
3094                 element);
3095
3096           /* do not set "num_entities" here to prevent reading behind buffer */
3097
3098           *(int *)(conf[i].num_entities) = 1;   /* at least one is required */
3099         }
3100         else
3101         {
3102           *(int *)(conf[i].num_entities) = num_entities;
3103         }
3104
3105         element_found = TRUE;
3106
3107         if (data_type == TYPE_STRING)
3108         {
3109           char *string = (char *)(conf[i].value);
3110           int j;
3111
3112           for (j = 0; j < max_num_entities; j++)
3113             string[j] = (j < num_entities ? buffer[j] : '\0');
3114         }
3115         else if (data_type == TYPE_ELEMENT_LIST)
3116         {
3117           int *element_array = (int *)(conf[i].value);
3118           int j;
3119
3120           for (j = 0; j < num_entities; j++)
3121             element_array[j] =
3122               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3123         }
3124         else if (data_type == TYPE_CONTENT_LIST)
3125         {
3126           struct Content *content= (struct Content *)(conf[i].value);
3127           int c, x, y;
3128
3129           for (c = 0; c < num_entities; c++)
3130             for (y = 0; y < 3; y++)
3131               for (x = 0; x < 3; x++)
3132                 content[c].e[x][y] =
3133                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3134         }
3135         else
3136           element_found = FALSE;
3137
3138         break;
3139       }
3140     }
3141
3142     checked_free(buffer);
3143
3144     micro_chunk_size += 2 + num_bytes;
3145   }
3146   else          /* constant size configuration data (1, 2 or 4 bytes) */
3147   {
3148     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3149                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3150                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3151
3152     for (i = 0; conf[i].data_type != -1; i++)
3153     {
3154       if (conf[i].element == element &&
3155           conf[i].conf_type == conf_type)
3156       {
3157         int data_type = conf[i].data_type;
3158
3159         if (data_type == TYPE_ELEMENT)
3160           value = getMappedElement(value);
3161
3162         if (data_type == TYPE_BOOLEAN)
3163           *(boolean *)(conf[i].value) = value;
3164         else
3165           *(int *)    (conf[i].value) = value;
3166
3167         element_found = TRUE;
3168
3169         break;
3170       }
3171     }
3172
3173     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3174   }
3175
3176   if (!element_found)
3177   {
3178     char *error_conf_chunk_bytes =
3179       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3180        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3181        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3182     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3183     int error_element = real_element;
3184
3185     Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3186           error_conf_chunk_bytes, error_conf_chunk_token,
3187           error_element, EL_NAME(error_element));
3188   }
3189
3190   return micro_chunk_size;
3191 }
3192
3193 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3194 {
3195   int real_chunk_size = 0;
3196
3197   li = *level;          /* copy level data into temporary buffer */
3198
3199   while (!checkEndOfFile(file))
3200   {
3201     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3202
3203     if (real_chunk_size >= chunk_size)
3204       break;
3205   }
3206
3207   *level = li;          /* copy temporary buffer back to level data */
3208
3209   return real_chunk_size;
3210 }
3211
3212 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3213 {
3214   int real_chunk_size = 0;
3215
3216   li = *level;          /* copy level data into temporary buffer */
3217
3218   while (!checkEndOfFile(file))
3219   {
3220     int element = getMappedElement(getFile16BitBE(file));
3221
3222     real_chunk_size += 2;
3223     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3224                                             element, element);
3225     if (real_chunk_size >= chunk_size)
3226       break;
3227   }
3228
3229   *level = li;          /* copy temporary buffer back to level data */
3230
3231   return real_chunk_size;
3232 }
3233
3234 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3235 {
3236   int real_chunk_size = 0;
3237
3238   li = *level;          /* copy level data into temporary buffer */
3239
3240   while (!checkEndOfFile(file))
3241   {
3242     int element = getMappedElement(getFile16BitBE(file));
3243
3244     real_chunk_size += 2;
3245     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3246                                             element, element);
3247     if (real_chunk_size >= chunk_size)
3248       break;
3249   }
3250
3251   *level = li;          /* copy temporary buffer back to level data */
3252
3253   return real_chunk_size;
3254 }
3255
3256 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3257 {
3258   int element = getMappedElement(getFile16BitBE(file));
3259   int envelope_nr = element - EL_ENVELOPE_1;
3260   int real_chunk_size = 2;
3261
3262   while (!checkEndOfFile(file))
3263   {
3264     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3265                                             -1, element);
3266
3267     if (real_chunk_size >= chunk_size)
3268       break;
3269   }
3270
3271   level->envelope[envelope_nr] = xx_envelope;
3272
3273   return real_chunk_size;
3274 }
3275
3276 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3277 {
3278   int element = getMappedElement(getFile16BitBE(file));
3279   int real_chunk_size = 2;
3280   struct ElementInfo *ei = &element_info[element];
3281   int i;
3282
3283   xx_ei = *ei;          /* copy element data into temporary buffer */
3284
3285   xx_ei.num_change_pages = -1;
3286
3287   while (!checkEndOfFile(file))
3288   {
3289     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3290                                             -1, element);
3291     if (xx_ei.num_change_pages != -1)
3292       break;
3293
3294     if (real_chunk_size >= chunk_size)
3295       break;
3296   }
3297
3298   *ei = xx_ei;
3299
3300   if (ei->num_change_pages == -1)
3301   {
3302     Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3303           EL_NAME(element));
3304
3305     ei->num_change_pages = 1;
3306
3307     setElementChangePages(ei, 1);
3308     setElementChangeInfoToDefaults(ei->change);
3309
3310     return real_chunk_size;
3311   }
3312
3313   /* initialize number of change pages stored for this custom element */
3314   setElementChangePages(ei, ei->num_change_pages);
3315   for (i = 0; i < ei->num_change_pages; i++)
3316     setElementChangeInfoToDefaults(&ei->change_page[i]);
3317
3318   /* start with reading properties for the first change page */
3319   xx_current_change_page = 0;
3320
3321   while (!checkEndOfFile(file))
3322   {
3323     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3324
3325     xx_change = *change;        /* copy change data into temporary buffer */
3326
3327     resetEventBits();           /* reset bits; change page might have changed */
3328
3329     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3330                                             -1, element);
3331
3332     *change = xx_change;
3333
3334     setEventFlagsFromEventBits(change);
3335
3336     if (real_chunk_size >= chunk_size)
3337       break;
3338   }
3339
3340   return real_chunk_size;
3341 }
3342
3343 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3344 {
3345   int element = getMappedElement(getFile16BitBE(file));
3346   int real_chunk_size = 2;
3347   struct ElementInfo *ei = &element_info[element];
3348   struct ElementGroupInfo *group = ei->group;
3349
3350   xx_ei = *ei;          /* copy element data into temporary buffer */
3351   xx_group = *group;    /* copy group data into temporary buffer */
3352
3353   while (!checkEndOfFile(file))
3354   {
3355     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3356                                             -1, element);
3357
3358     if (real_chunk_size >= chunk_size)
3359       break;
3360   }
3361
3362   *ei = xx_ei;
3363   *group = xx_group;
3364
3365   return real_chunk_size;
3366 }
3367
3368 #else
3369
3370 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
3371 {
3372   level->file_version = getFileVersion(file);
3373   level->game_version = getFileVersion(file);
3374
3375   return chunk_size;
3376 }
3377
3378 static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
3379 {
3380   level->creation_date.year  = getFile16BitBE(file);
3381   level->creation_date.month = getFile8Bit(file);
3382   level->creation_date.day   = getFile8Bit(file);
3383
3384   level->creation_date.src   = DATE_SRC_LEVELFILE;
3385
3386   return chunk_size;
3387 }
3388
3389 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
3390 {
3391   int initial_player_stepsize;
3392   int initial_player_gravity;
3393   int i, x, y;
3394
3395   level->fieldx = getFile8Bit(file);
3396   level->fieldy = getFile8Bit(file);
3397
3398   level->time           = getFile16BitBE(file);
3399   level->gems_needed    = getFile16BitBE(file);
3400
3401   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3402     level->name[i] = getFile8Bit(file);
3403   level->name[MAX_LEVEL_NAME_LEN] = 0;
3404
3405   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3406     level->score[i] = getFile8Bit(file);
3407
3408   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3409   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3410     for (y = 0; y < 3; y++)
3411       for (x = 0; x < 3; x++)
3412         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3413
3414   level->amoeba_speed           = getFile8Bit(file);
3415   level->time_magic_wall        = getFile8Bit(file);
3416   level->time_wheel             = getFile8Bit(file);
3417   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3418
3419   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3420                                    STEPSIZE_NORMAL);
3421
3422   for (i = 0; i < MAX_PLAYERS; i++)
3423     level->initial_player_stepsize[i] = initial_player_stepsize;
3424
3425   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3426
3427   for (i = 0; i < MAX_PLAYERS; i++)
3428     level->initial_player_gravity[i] = initial_player_gravity;
3429
3430   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3431   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3432
3433   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3434
3435   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3436   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3437   level->can_move_into_acid_bits = getFile32BitBE(file);
3438   level->dont_collide_with_bits = getFile8Bit(file);
3439
3440   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3441   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3442
3443   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3444   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3445   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3446
3447   level->game_engine_type       = getFile8Bit(file);
3448
3449   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3450
3451   return chunk_size;
3452 }
3453
3454 static int LoadLevel_NAME(FILE *file, int chunk_size, struct LevelInfo *level)
3455 {
3456   int i;
3457
3458   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3459     level->name[i] = getFile8Bit(file);
3460   level->name[MAX_LEVEL_NAME_LEN] = 0;
3461
3462   return chunk_size;
3463 }
3464
3465 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
3466 {
3467   int i;
3468
3469   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3470     level->author[i] = getFile8Bit(file);
3471   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3472
3473   return chunk_size;
3474 }
3475
3476 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
3477 {
3478   int x, y;
3479   int chunk_size_expected = level->fieldx * level->fieldy;
3480
3481   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3482      stored with 16-bit encoding (and should be twice as big then).
3483      Even worse, playfield data was stored 16-bit when only yamyam content
3484      contained 16-bit elements and vice versa. */
3485
3486   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3487     chunk_size_expected *= 2;
3488
3489   if (chunk_size_expected != chunk_size)
3490   {
3491     ReadUnusedBytesFromFile(file, chunk_size);
3492     return chunk_size_expected;
3493   }
3494
3495   for (y = 0; y < level->fieldy; y++)
3496     for (x = 0; x < level->fieldx; x++)
3497       level->field[x][y] =
3498         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3499                          getFile8Bit(file));
3500   return chunk_size;
3501 }
3502
3503 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
3504 {
3505   int i, x, y;
3506   int header_size = 4;
3507   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3508   int chunk_size_expected = header_size + content_size;
3509
3510   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3511      stored with 16-bit encoding (and should be twice as big then).
3512      Even worse, playfield data was stored 16-bit when only yamyam content
3513      contained 16-bit elements and vice versa. */
3514
3515   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3516     chunk_size_expected += content_size;
3517
3518   if (chunk_size_expected != chunk_size)
3519   {
3520     ReadUnusedBytesFromFile(file, chunk_size);
3521     return chunk_size_expected;
3522   }
3523
3524   getFile8Bit(file);
3525   level->num_yamyam_contents = getFile8Bit(file);
3526   getFile8Bit(file);
3527   getFile8Bit(file);
3528
3529   /* correct invalid number of content fields -- should never happen */
3530   if (level->num_yamyam_contents < 1 ||
3531       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3532     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3533
3534   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3535     for (y = 0; y < 3; y++)
3536       for (x = 0; x < 3; x++)
3537         level->yamyam_content[i].e[x][y] =
3538           getMappedElement(level->encoding_16bit_field ?
3539                            getFile16BitBE(file) : getFile8Bit(file));
3540   return chunk_size;
3541 }
3542
3543 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
3544 {
3545   int i, x, y;
3546   int element;
3547   int num_contents;
3548 #if 0
3549   int content_xsize, content_ysize;
3550 #endif
3551   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3552
3553   element = getMappedElement(getFile16BitBE(file));
3554   num_contents = getFile8Bit(file);
3555 #if 1
3556   getFile8Bit(file);    /* content x size (unused) */
3557   getFile8Bit(file);    /* content y size (unused) */
3558 #else
3559   content_xsize = getFile8Bit(file);
3560   content_ysize = getFile8Bit(file);
3561 #endif
3562
3563   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3564
3565   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3566     for (y = 0; y < 3; y++)
3567       for (x = 0; x < 3; x++)
3568         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3569
3570   /* correct invalid number of content fields -- should never happen */
3571   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3572     num_contents = STD_ELEMENT_CONTENTS;
3573
3574   if (element == EL_YAMYAM)
3575   {
3576     level->num_yamyam_contents = num_contents;
3577
3578     for (i = 0; i < num_contents; i++)
3579       for (y = 0; y < 3; y++)
3580         for (x = 0; x < 3; x++)
3581           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3582   }
3583   else if (element == EL_BD_AMOEBA)
3584   {
3585     level->amoeba_content = content_array[0][0][0];
3586   }
3587   else
3588   {
3589     Error(ERR_WARN, "cannot load content for element '%d'", element);
3590   }
3591
3592   return chunk_size;
3593 }
3594
3595 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
3596 {
3597   int i;
3598   int element;
3599   int envelope_nr;
3600   int envelope_len;
3601   int chunk_size_expected;
3602
3603   element = getMappedElement(getFile16BitBE(file));
3604   if (!IS_ENVELOPE(element))
3605     element = EL_ENVELOPE_1;
3606
3607   envelope_nr = element - EL_ENVELOPE_1;
3608
3609   envelope_len = getFile16BitBE(file);
3610
3611   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3612   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3613
3614   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3615
3616   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3617   if (chunk_size_expected != chunk_size)
3618   {
3619     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3620     return chunk_size_expected;
3621   }
3622
3623   for (i = 0; i < envelope_len; i++)
3624     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3625
3626   return chunk_size;
3627 }
3628
3629 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
3630 {
3631   int num_changed_custom_elements = getFile16BitBE(file);
3632   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3633   int i;
3634
3635   if (chunk_size_expected != chunk_size)
3636   {
3637     ReadUnusedBytesFromFile(file, chunk_size - 2);
3638     return chunk_size_expected;
3639   }
3640
3641   for (i = 0; i < num_changed_custom_elements; i++)
3642   {
3643     int element = getMappedElement(getFile16BitBE(file));
3644     int properties = getFile32BitBE(file);
3645
3646     if (IS_CUSTOM_ELEMENT(element))
3647       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3648     else
3649       Error(ERR_WARN, "invalid custom element number %d", element);
3650
3651     /* older game versions that wrote level files with CUS1 chunks used
3652        different default push delay values (not yet stored in level file) */
3653     element_info[element].push_delay_fixed = 2;
3654     element_info[element].push_delay_random = 8;
3655   }
3656
3657   return chunk_size;
3658 }
3659
3660 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
3661 {
3662   int num_changed_custom_elements = getFile16BitBE(file);
3663   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3664   int i;
3665
3666   if (chunk_size_expected != chunk_size)
3667   {
3668     ReadUnusedBytesFromFile(file, chunk_size - 2);
3669     return chunk_size_expected;
3670   }
3671
3672   for (i = 0; i < num_changed_custom_elements; i++)
3673   {
3674     int element = getMappedElement(getFile16BitBE(file));
3675     int custom_target_element = getMappedElement(getFile16BitBE(file));
3676
3677     if (IS_CUSTOM_ELEMENT(element))
3678       element_info[element].change->target_element = custom_target_element;
3679     else
3680       Error(ERR_WARN, "invalid custom element number %d", element);
3681   }
3682
3683   return chunk_size;
3684 }
3685
3686 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
3687 {
3688   int num_changed_custom_elements = getFile16BitBE(file);
3689   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3690   int i, j, x, y;
3691
3692   if (chunk_size_expected != chunk_size)
3693   {
3694     ReadUnusedBytesFromFile(file, chunk_size - 2);
3695     return chunk_size_expected;
3696   }
3697
3698   for (i = 0; i < num_changed_custom_elements; i++)
3699   {
3700     int element = getMappedElement(getFile16BitBE(file));
3701     struct ElementInfo *ei = &element_info[element];
3702     unsigned int event_bits;
3703
3704     if (!IS_CUSTOM_ELEMENT(element))
3705     {
3706       Error(ERR_WARN, "invalid custom element number %d", element);
3707
3708       element = EL_INTERNAL_DUMMY;
3709     }
3710
3711     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3712       ei->description[j] = getFile8Bit(file);
3713     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3714
3715     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3716
3717     /* some free bytes for future properties and padding */
3718     ReadUnusedBytesFromFile(file, 7);
3719
3720     ei->use_gfx_element = getFile8Bit(file);
3721     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3722
3723     ei->collect_score_initial = getFile8Bit(file);
3724     ei->collect_count_initial = getFile8Bit(file);
3725
3726     ei->push_delay_fixed = getFile16BitBE(file);
3727     ei->push_delay_random = getFile16BitBE(file);
3728     ei->move_delay_fixed = getFile16BitBE(file);
3729     ei->move_delay_random = getFile16BitBE(file);
3730
3731     ei->move_pattern = getFile16BitBE(file);
3732     ei->move_direction_initial = getFile8Bit(file);
3733     ei->move_stepsize = getFile8Bit(file);
3734
3735     for (y = 0; y < 3; y++)
3736       for (x = 0; x < 3; x++)
3737         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3738
3739     event_bits = getFile32BitBE(file);
3740     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3741       if (event_bits & (1 << j))
3742         ei->change->has_event[j] = TRUE;
3743
3744     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3745
3746     ei->change->delay_fixed = getFile16BitBE(file);
3747     ei->change->delay_random = getFile16BitBE(file);
3748     ei->change->delay_frames = getFile16BitBE(file);
3749
3750     ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
3751
3752     ei->change->explode = getFile8Bit(file);
3753     ei->change->use_target_content = getFile8Bit(file);
3754     ei->change->only_if_complete = getFile8Bit(file);
3755     ei->change->use_random_replace = getFile8Bit(file);
3756
3757     ei->change->random_percentage = getFile8Bit(file);
3758     ei->change->replace_when = getFile8Bit(file);
3759
3760     for (y = 0; y < 3; y++)
3761       for (x = 0; x < 3; x++)
3762         ei->change->target_content.e[x][y] =
3763           getMappedElement(getFile16BitBE(file));
3764
3765     ei->slippery_type = getFile8Bit(file);
3766
3767     /* some free bytes for future properties and padding */
3768     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3769
3770     /* mark that this custom element has been modified */
3771     ei->modified_settings = TRUE;
3772   }
3773
3774   return chunk_size;
3775 }
3776
3777 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
3778 {
3779   struct ElementInfo *ei;
3780   int chunk_size_expected;
3781   int element;
3782   int i, j, x, y;
3783
3784   /* ---------- custom element base property values (96 bytes) ------------- */
3785
3786   element = getMappedElement(getFile16BitBE(file));
3787
3788   if (!IS_CUSTOM_ELEMENT(element))
3789   {
3790     Error(ERR_WARN, "invalid custom element number %d", element);
3791
3792     ReadUnusedBytesFromFile(file, chunk_size - 2);
3793     return chunk_size;
3794   }
3795
3796   ei = &element_info[element];
3797
3798   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3799     ei->description[i] = getFile8Bit(file);
3800   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3801
3802   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3803
3804   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
3805
3806   ei->num_change_pages = getFile8Bit(file);
3807
3808   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3809   if (chunk_size_expected != chunk_size)
3810   {
3811     ReadUnusedBytesFromFile(file, chunk_size - 43);
3812     return chunk_size_expected;
3813   }
3814
3815   ei->ce_value_fixed_initial = getFile16BitBE(file);
3816   ei->ce_value_random_initial = getFile16BitBE(file);
3817   ei->use_last_ce_value = getFile8Bit(file);
3818
3819   ei->use_gfx_element = getFile8Bit(file);
3820   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3821
3822   ei->collect_score_initial = getFile8Bit(file);
3823   ei->collect_count_initial = getFile8Bit(file);
3824
3825   ei->drop_delay_fixed = getFile8Bit(file);
3826   ei->push_delay_fixed = getFile8Bit(file);
3827   ei->drop_delay_random = getFile8Bit(file);
3828   ei->push_delay_random = getFile8Bit(file);
3829   ei->move_delay_fixed = getFile16BitBE(file);
3830   ei->move_delay_random = getFile16BitBE(file);
3831
3832   /* bits 0 - 15 of "move_pattern" ... */
3833   ei->move_pattern = getFile16BitBE(file);
3834   ei->move_direction_initial = getFile8Bit(file);
3835   ei->move_stepsize = getFile8Bit(file);
3836
3837   ei->slippery_type = getFile8Bit(file);
3838
3839   for (y = 0; y < 3; y++)
3840     for (x = 0; x < 3; x++)
3841       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3842
3843   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3844   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3845   ei->move_leave_type = getFile8Bit(file);
3846
3847   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
3848   ei->move_pattern |= (getFile16BitBE(file) << 16);
3849
3850   ei->access_direction = getFile8Bit(file);
3851
3852   ei->explosion_delay = getFile8Bit(file);
3853   ei->ignition_delay = getFile8Bit(file);
3854   ei->explosion_type = getFile8Bit(file);
3855
3856   /* some free bytes for future custom property values and padding */
3857   ReadUnusedBytesFromFile(file, 1);
3858
3859   /* ---------- change page property values (48 bytes) --------------------- */
3860
3861   setElementChangePages(ei, ei->num_change_pages);
3862
3863   for (i = 0; i < ei->num_change_pages; i++)
3864   {
3865     struct ElementChangeInfo *change = &ei->change_page[i];
3866     unsigned int event_bits;
3867
3868     /* always start with reliable default values */
3869     setElementChangeInfoToDefaults(change);
3870
3871     /* bits 0 - 31 of "has_event[]" ... */
3872     event_bits = getFile32BitBE(file);
3873     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3874       if (event_bits & (1 << j))
3875         change->has_event[j] = TRUE;
3876
3877     change->target_element = getMappedElement(getFile16BitBE(file));
3878
3879     change->delay_fixed = getFile16BitBE(file);
3880     change->delay_random = getFile16BitBE(file);
3881     change->delay_frames = getFile16BitBE(file);
3882
3883     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3884
3885     change->explode = getFile8Bit(file);
3886     change->use_target_content = getFile8Bit(file);
3887     change->only_if_complete = getFile8Bit(file);
3888     change->use_random_replace = getFile8Bit(file);
3889
3890     change->random_percentage = getFile8Bit(file);
3891     change->replace_when = getFile8Bit(file);
3892
3893     for (y = 0; y < 3; y++)
3894       for (x = 0; x < 3; x++)
3895         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3896
3897     change->can_change = getFile8Bit(file);
3898
3899     change->trigger_side = getFile8Bit(file);
3900
3901     change->trigger_player = getFile8Bit(file);
3902     change->trigger_page = getFile8Bit(file);
3903
3904     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3905                             CH_PAGE_ANY : (1 << change->trigger_page));
3906
3907     change->has_action = getFile8Bit(file);
3908     change->action_type = getFile8Bit(file);
3909     change->action_mode = getFile8Bit(file);
3910     change->action_arg = getFile16BitBE(file);
3911
3912     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
3913     event_bits = getFile8Bit(file);
3914     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3915       if (event_bits & (1 << (j - 32)))
3916         change->has_event[j] = TRUE;
3917   }
3918
3919   /* mark this custom element as modified */
3920   ei->modified_settings = TRUE;
3921
3922   return chunk_size;
3923 }
3924
3925 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
3926 {
3927   struct ElementInfo *ei;
3928   struct ElementGroupInfo *group;
3929   int element;
3930   int i;
3931
3932   element = getMappedElement(getFile16BitBE(file));
3933
3934   if (!IS_GROUP_ELEMENT(element))
3935   {
3936     Error(ERR_WARN, "invalid group element number %d", element);
3937
3938     ReadUnusedBytesFromFile(file, chunk_size - 2);
3939     return chunk_size;
3940   }
3941
3942   ei = &element_info[element];
3943
3944   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3945     ei->description[i] = getFile8Bit(file);
3946   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3947
3948   group = element_info[element].group;
3949
3950   group->num_elements = getFile8Bit(file);
3951
3952   ei->use_gfx_element = getFile8Bit(file);
3953   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3954
3955   group->choice_mode = getFile8Bit(file);
3956
3957   /* some free bytes for future values and padding */
3958   ReadUnusedBytesFromFile(file, 3);
3959
3960   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3961     group->element[i] = getMappedElement(getFile16BitBE(file));
3962
3963   /* mark this group element as modified */
3964   element_info[element].modified_settings = TRUE;
3965
3966   return chunk_size;
3967 }
3968
3969 static int LoadLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *conf,
3970                                 int element, int real_element)
3971 {
3972   int micro_chunk_size = 0;
3973   int conf_type = getFile8Bit(file);
3974   int byte_mask = conf_type & CONF_MASK_BYTES;
3975   boolean element_found = FALSE;
3976   int i;
3977
3978   micro_chunk_size += 1;
3979
3980   if (byte_mask == CONF_MASK_MULTI_BYTES)
3981   {
3982     int num_bytes = getFile16BitBE(file);
3983     byte *buffer = checked_malloc(num_bytes);
3984
3985     ReadBytesFromFile(file, buffer, num_bytes);
3986
3987     for (i = 0; conf[i].data_type != -1; i++)
3988     {
3989       if (conf[i].element == element &&
3990           conf[i].conf_type == conf_type)
3991       {
3992         int data_type = conf[i].data_type;
3993         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3994         int max_num_entities = conf[i].max_num_entities;
3995
3996         if (num_entities > max_num_entities)
3997         {
3998           Error(ERR_WARN,
3999                 "truncating number of entities for element %d from %d to %d",
4000                 element, num_entities, max_num_entities);
4001
4002           num_entities = max_num_entities;
4003         }
4004
4005         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
4006                                   data_type == TYPE_CONTENT_LIST))
4007         {
4008           /* for element and content lists, zero entities are not allowed */
4009           Error(ERR_WARN, "found empty list of entities for element %d",
4010                 element);
4011
4012           /* do not set "num_entities" here to prevent reading behind buffer */
4013
4014           *(int *)(conf[i].num_entities) = 1;   /* at least one is required */
4015         }
4016         else
4017         {
4018           *(int *)(conf[i].num_entities) = num_entities;
4019         }
4020
4021         element_found = TRUE;
4022
4023         if (data_type == TYPE_STRING)
4024         {
4025           char *string = (char *)(conf[i].value);
4026           int j;
4027
4028           for (j = 0; j < max_num_entities; j++)
4029             string[j] = (j < num_entities ? buffer[j] : '\0');
4030         }
4031         else if (data_type == TYPE_ELEMENT_LIST)
4032         {
4033           int *element_array = (int *)(conf[i].value);
4034           int j;
4035
4036           for (j = 0; j < num_entities; j++)
4037             element_array[j] =
4038               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
4039         }
4040         else if (data_type == TYPE_CONTENT_LIST)
4041         {
4042           struct Content *content= (struct Content *)(conf[i].value);
4043           int c, x, y;
4044
4045           for (c = 0; c < num_entities; c++)
4046             for (y = 0; y < 3; y++)
4047               for (x = 0; x < 3; x++)
4048                 content[c].e[x][y] =
4049                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
4050         }
4051         else
4052           element_found = FALSE;
4053
4054         break;
4055       }
4056     }
4057
4058     checked_free(buffer);
4059
4060     micro_chunk_size += 2 + num_bytes;
4061   }
4062   else          /* constant size configuration data (1, 2 or 4 bytes) */
4063   {
4064     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
4065                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
4066                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
4067
4068     for (i = 0; conf[i].data_type != -1; i++)
4069     {
4070       if (conf[i].element == element &&
4071           conf[i].conf_type == conf_type)
4072       {
4073         int data_type = conf[i].data_type;
4074
4075         if (data_type == TYPE_ELEMENT)
4076           value = getMappedElement(value);
4077
4078         if (data_type == TYPE_BOOLEAN)
4079           *(boolean *)(conf[i].value) = value;
4080         else
4081           *(int *)    (conf[i].value) = value;
4082
4083         element_found = TRUE;
4084
4085         break;
4086       }
4087     }
4088
4089     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
4090   }
4091
4092   if (!element_found)
4093   {
4094     char *error_conf_chunk_bytes =
4095       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
4096        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
4097        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
4098     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
4099     int error_element = real_element;
4100
4101     Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
4102           error_conf_chunk_bytes, error_conf_chunk_token,
4103           error_element, EL_NAME(error_element));
4104   }
4105
4106   return micro_chunk_size;
4107 }
4108
4109 static int LoadLevel_INFO(FILE *file, int chunk_size, struct LevelInfo *level)
4110 {
4111   int real_chunk_size = 0;
4112
4113   li = *level;          /* copy level data into temporary buffer */
4114
4115   while (!feof(file))
4116   {
4117     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
4118
4119     if (real_chunk_size >= chunk_size)
4120       break;
4121   }
4122
4123   *level = li;          /* copy temporary buffer back to level data */
4124
4125   return real_chunk_size;
4126 }
4127
4128 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
4129 {
4130   int real_chunk_size = 0;
4131
4132   li = *level;          /* copy level data into temporary buffer */
4133
4134   while (!feof(file))
4135   {
4136     int element = getMappedElement(getFile16BitBE(file));
4137
4138     real_chunk_size += 2;
4139     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
4140                                             element, element);
4141     if (real_chunk_size >= chunk_size)
4142       break;
4143   }
4144
4145   *level = li;          /* copy temporary buffer back to level data */
4146
4147   return real_chunk_size;
4148 }
4149
4150 static int LoadLevel_ELEM(FILE *file, int chunk_size, struct LevelInfo *level)
4151 {
4152   int real_chunk_size = 0;
4153
4154   li = *level;          /* copy level data into temporary buffer */
4155
4156   while (!feof(file))
4157   {
4158     int element = getMappedElement(getFile16BitBE(file));
4159
4160     real_chunk_size += 2;
4161     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
4162                                             element, element);
4163     if (real_chunk_size >= chunk_size)
4164       break;
4165   }
4166
4167   *level = li;          /* copy temporary buffer back to level data */
4168
4169   return real_chunk_size;
4170 }
4171
4172 static int LoadLevel_NOTE(FILE *file, int chunk_size, struct LevelInfo *level)
4173 {
4174   int element = getMappedElement(getFile16BitBE(file));
4175   int envelope_nr = element - EL_ENVELOPE_1;
4176   int real_chunk_size = 2;
4177
4178   while (!feof(file))
4179   {
4180     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
4181                                             -1, element);
4182
4183     if (real_chunk_size >= chunk_size)
4184       break;
4185   }
4186
4187   level->envelope[envelope_nr] = xx_envelope;
4188
4189   return real_chunk_size;
4190 }
4191
4192 static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
4193 {
4194   int element = getMappedElement(getFile16BitBE(file));
4195   int real_chunk_size = 2;
4196   struct ElementInfo *ei = &element_info[element];
4197   int i;
4198
4199   xx_ei = *ei;          /* copy element data into temporary buffer */
4200
4201   xx_ei.num_change_pages = -1;
4202
4203   while (!feof(file))
4204   {
4205     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
4206                                             -1, element);
4207     if (xx_ei.num_change_pages != -1)
4208       break;
4209
4210     if (real_chunk_size >= chunk_size)
4211       break;
4212   }
4213
4214   *ei = xx_ei;
4215
4216   if (ei->num_change_pages == -1)
4217   {
4218     Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4219           EL_NAME(element));
4220
4221     ei->num_change_pages = 1;
4222
4223     setElementChangePages(ei, 1);
4224     setElementChangeInfoToDefaults(ei->change);
4225
4226     return real_chunk_size;
4227   }
4228
4229   /* initialize number of change pages stored for this custom element */
4230   setElementChangePages(ei, ei->num_change_pages);
4231   for (i = 0; i < ei->num_change_pages; i++)
4232     setElementChangeInfoToDefaults(&ei->change_page[i]);
4233
4234   /* start with reading properties for the first change page */
4235   xx_current_change_page = 0;
4236
4237   while (!feof(file))
4238   {
4239     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4240
4241     xx_change = *change;        /* copy change data into temporary buffer */
4242
4243     resetEventBits();           /* reset bits; change page might have changed */
4244
4245     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4246                                             -1, element);
4247
4248     *change = xx_change;
4249
4250     setEventFlagsFromEventBits(change);
4251
4252     if (real_chunk_size >= chunk_size)
4253       break;
4254   }
4255
4256   return real_chunk_size;
4257 }
4258
4259 static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
4260 {
4261   int element = getMappedElement(getFile16BitBE(file));
4262   int real_chunk_size = 2;
4263   struct ElementInfo *ei = &element_info[element];
4264   struct ElementGroupInfo *group = ei->group;
4265
4266   xx_ei = *ei;          /* copy element data into temporary buffer */
4267   xx_group = *group;    /* copy group data into temporary buffer */
4268
4269   while (!feof(file))
4270   {
4271     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4272                                             -1, element);
4273
4274     if (real_chunk_size >= chunk_size)
4275       break;
4276   }
4277
4278   *ei = xx_ei;
4279   *group = xx_group;
4280
4281   return real_chunk_size;
4282 }
4283
4284 #endif
4285
4286 #if 1
4287
4288 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4289                                       struct LevelFileInfo *level_file_info,
4290                                       boolean level_info_only)
4291 {
4292   char *filename = level_file_info->filename;
4293   char cookie[MAX_LINE_LEN];
4294   char chunk_name[CHUNK_ID_LEN + 1];
4295   int chunk_size;
4296   File *file;
4297
4298   if (!(file = openFile(filename, MODE_READ)))
4299   {
4300     level->no_valid_file = TRUE;
4301
4302 #if 1
4303     if (!level_info_only)
4304       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4305 #else
4306     if (level != &level_template)
4307       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4308 #endif
4309
4310     return;
4311   }
4312
4313   getFileChunkBE(file, chunk_name, NULL);
4314   if (strEqual(chunk_name, "RND1"))
4315   {
4316     getFile32BitBE(file);               /* not used */
4317
4318     getFileChunkBE(file, chunk_name, NULL);
4319     if (!strEqual(chunk_name, "CAVE"))
4320     {
4321       level->no_valid_file = TRUE;
4322
4323       Error(ERR_WARN, "unknown format of level file '%s'", filename);
4324
4325       closeFile(file);
4326
4327       return;
4328     }
4329   }
4330   else  /* check for pre-2.0 file format with cookie string */
4331   {
4332     strcpy(cookie, chunk_name);
4333     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4334       cookie[4] = '\0';
4335     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4336       cookie[strlen(cookie) - 1] = '\0';
4337
4338     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4339     {
4340       level->no_valid_file = TRUE;
4341
4342       Error(ERR_WARN, "unknown format of level file '%s'", filename);
4343
4344       closeFile(file);
4345
4346       return;
4347     }
4348
4349     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4350     {
4351       level->no_valid_file = TRUE;
4352
4353       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
4354
4355       closeFile(file);
4356
4357       return;
4358     }
4359
4360     /* pre-2.0 level files have no game version, so use file version here */
4361     level->game_version = level->file_version;
4362   }
4363
4364   if (level->file_version < FILE_VERSION_1_2)
4365   {
4366     /* level files from versions before 1.2.0 without chunk structure */
4367     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4368     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4369   }
4370   else
4371   {
4372     static struct
4373     {
4374       char *name;
4375       int size;
4376       int (*loader)(File *, int, struct LevelInfo *);
4377     }
4378     chunk_info[] =
4379     {
4380       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4381       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4382       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4383       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4384       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4385       { "INFO", -1,                     LoadLevel_INFO },
4386       { "BODY", -1,                     LoadLevel_BODY },
4387       { "CONT", -1,                     LoadLevel_CONT },
4388       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4389       { "CNT3", -1,                     LoadLevel_CNT3 },
4390       { "CUS1", -1,                     LoadLevel_CUS1 },
4391       { "CUS2", -1,                     LoadLevel_CUS2 },
4392       { "CUS3", -1,                     LoadLevel_CUS3 },
4393       { "CUS4", -1,                     LoadLevel_CUS4 },
4394       { "GRP1", -1,                     LoadLevel_GRP1 },
4395       { "CONF", -1,                     LoadLevel_CONF },
4396       { "ELEM", -1,                     LoadLevel_ELEM },
4397       { "NOTE", -1,                     LoadLevel_NOTE },
4398       { "CUSX", -1,                     LoadLevel_CUSX },
4399       { "GRPX", -1,                     LoadLevel_GRPX },
4400
4401       {  NULL,  0,                      NULL }
4402     };
4403
4404     while (getFileChunkBE(file, chunk_name, &chunk_size))
4405     {
4406       int i = 0;
4407
4408       while (chunk_info[i].name != NULL &&
4409              !strEqual(chunk_name, chunk_info[i].name))
4410         i++;
4411
4412       if (chunk_info[i].name == NULL)
4413       {
4414         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
4415               chunk_name, filename);
4416         ReadUnusedBytesFromFile(file, chunk_size);
4417       }
4418       else if (chunk_info[i].size != -1 &&
4419                chunk_info[i].size != chunk_size)
4420       {
4421         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4422               chunk_size, chunk_name, filename);
4423         ReadUnusedBytesFromFile(file, chunk_size);
4424       }
4425       else
4426       {
4427         /* call function to load this level chunk */
4428         int chunk_size_expected =
4429           (chunk_info[i].loader)(file, chunk_size, level);
4430
4431         /* the size of some chunks cannot be checked before reading other
4432            chunks first (like "HEAD" and "BODY") that contain some header
4433            information, so check them here */
4434         if (chunk_size_expected != chunk_size)
4435         {
4436           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4437                 chunk_size, chunk_name, filename);
4438         }
4439       }
4440     }
4441   }
4442
4443   closeFile(file);
4444 }
4445
4446 #else
4447
4448 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4449                                       struct LevelFileInfo *level_file_info,
4450                                       boolean level_info_only)
4451 {
4452   char *filename = level_file_info->filename;
4453   char cookie[MAX_LINE_LEN];
4454   char chunk_name[CHUNK_ID_LEN + 1];
4455   int chunk_size;
4456   FILE *file;
4457
4458   if (!(file = fopen(filename, MODE_READ)))
4459   {
4460     level->no_valid_file = TRUE;
4461
4462 #if 1
4463     if (!level_info_only)
4464       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4465 #else
4466     if (level != &level_template)
4467       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4468 #endif
4469
4470     return;
4471   }
4472
4473   getFileChunkBE(file, chunk_name, NULL);
4474   if (strEqual(chunk_name, "RND1"))
4475   {
4476     getFile32BitBE(file);               /* not used */
4477
4478     getFileChunkBE(file, chunk_name, NULL);
4479     if (!strEqual(chunk_name, "CAVE"))
4480     {
4481       level->no_valid_file = TRUE;
4482
4483       Error(ERR_WARN, "unknown format of level file '%s'", filename);
4484       fclose(file);
4485       return;
4486     }
4487   }
4488   else  /* check for pre-2.0 file format with cookie string */
4489   {
4490     strcpy(cookie, chunk_name);
4491     if (fgets(&cookie[4], MAX_LINE_LEN - 4, file) == NULL)
4492       cookie[4] = '\0';
4493     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4494       cookie[strlen(cookie) - 1] = '\0';
4495
4496     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4497     {
4498       level->no_valid_file = TRUE;
4499
4500       Error(ERR_WARN, "unknown format of level file '%s'", filename);
4501       fclose(file);
4502       return;
4503     }
4504
4505     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4506     {
4507       level->no_valid_file = TRUE;
4508
4509       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
4510       fclose(file);
4511       return;
4512     }
4513
4514     /* pre-2.0 level files have no game version, so use file version here */
4515     level->game_version = level->file_version;
4516   }
4517
4518   if (level->file_version < FILE_VERSION_1_2)
4519   {
4520     /* level files from versions before 1.2.0 without chunk structure */
4521     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4522     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4523   }
4524   else
4525   {
4526     static struct
4527     {
4528       char *name;
4529       int size;
4530       int (*loader)(FILE *, int, struct LevelInfo *);
4531     }
4532     chunk_info[] =
4533     {
4534       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4535       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4536       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4537       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4538       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4539       { "INFO", -1,                     LoadLevel_INFO },
4540       { "BODY", -1,                     LoadLevel_BODY },
4541       { "CONT", -1,                     LoadLevel_CONT },
4542       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4543       { "CNT3", -1,                     LoadLevel_CNT3 },
4544       { "CUS1", -1,                     LoadLevel_CUS1 },
4545       { "CUS2", -1,                     LoadLevel_CUS2 },
4546       { "CUS3", -1,                     LoadLevel_CUS3 },
4547       { "CUS4", -1,                     LoadLevel_CUS4 },
4548       { "GRP1", -1,                     LoadLevel_GRP1 },
4549       { "CONF", -1,                     LoadLevel_CONF },
4550       { "ELEM", -1,                     LoadLevel_ELEM },
4551       { "NOTE", -1,                     LoadLevel_NOTE },
4552       { "CUSX", -1,                     LoadLevel_CUSX },
4553       { "GRPX", -1,                     LoadLevel_GRPX },
4554
4555       {  NULL,  0,                      NULL }
4556     };
4557
4558     while (getFileChunkBE(file, chunk_name, &chunk_size))
4559     {
4560       int i = 0;
4561
4562       while (chunk_info[i].name != NULL &&
4563              !strEqual(chunk_name, chunk_info[i].name))
4564         i++;
4565
4566       if (chunk_info[i].name == NULL)
4567       {
4568         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
4569               chunk_name, filename);
4570         ReadUnusedBytesFromFile(file, chunk_size);
4571       }
4572       else if (chunk_info[i].size != -1 &&
4573                chunk_info[i].size != chunk_size)
4574       {
4575         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4576               chunk_size, chunk_name, filename);
4577         ReadUnusedBytesFromFile(file, chunk_size);
4578       }
4579       else
4580       {
4581         /* call function to load this level chunk */
4582         int chunk_size_expected =
4583           (chunk_info[i].loader)(file, chunk_size, level);
4584
4585         /* the size of some chunks cannot be checked before reading other
4586            chunks first (like "HEAD" and "BODY") that contain some header
4587            information, so check them here */
4588         if (chunk_size_expected != chunk_size)
4589         {
4590           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
4591                 chunk_size, chunk_name, filename);
4592         }
4593       }
4594     }
4595   }
4596
4597   fclose(file);
4598 }
4599
4600 #endif
4601
4602
4603 /* ------------------------------------------------------------------------- */
4604 /* functions for loading EM level                                            */
4605 /* ------------------------------------------------------------------------- */
4606
4607 #if 0
4608
4609 static int map_em_element_yam(int element)
4610 {
4611   switch (element)
4612   {
4613     case 0x00:  return EL_EMPTY;
4614     case 0x01:  return EL_EMERALD;
4615     case 0x02:  return EL_DIAMOND;
4616     case 0x03:  return EL_ROCK;
4617     case 0x04:  return EL_ROBOT;
4618     case 0x05:  return EL_SPACESHIP_UP;
4619     case 0x06:  return EL_BOMB;
4620     case 0x07:  return EL_BUG_UP;
4621     case 0x08:  return EL_AMOEBA_DROP;
4622     case 0x09:  return EL_NUT;
4623     case 0x0a:  return EL_YAMYAM;
4624     case 0x0b:  return EL_QUICKSAND_FULL;
4625     case 0x0c:  return EL_SAND;
4626     case 0x0d:  return EL_WALL_SLIPPERY;
4627     case 0x0e:  return EL_STEELWALL;
4628     case 0x0f:  return EL_WALL;
4629     case 0x10:  return EL_EM_KEY_1;
4630     case 0x11:  return EL_EM_KEY_2;
4631     case 0x12:  return EL_EM_KEY_4;
4632     case 0x13:  return EL_EM_KEY_3;
4633     case 0x14:  return EL_MAGIC_WALL;
4634     case 0x15:  return EL_ROBOT_WHEEL;
4635     case 0x16:  return EL_DYNAMITE;
4636
4637     case 0x17:  return EL_EM_KEY_1;                     /* EMC */
4638     case 0x18:  return EL_BUG_UP;                       /* EMC */
4639     case 0x1a:  return EL_DIAMOND;                      /* EMC */
4640     case 0x1b:  return EL_EMERALD;                      /* EMC */
4641     case 0x25:  return EL_NUT;                          /* EMC */
4642     case 0x80:  return EL_EMPTY;                        /* EMC */
4643     case 0x85:  return EL_EM_KEY_1;                     /* EMC */
4644     case 0x86:  return EL_EM_KEY_2;                     /* EMC */
4645     case 0x87:  return EL_EM_KEY_4;                     /* EMC */
4646     case 0x88:  return EL_EM_KEY_3;                     /* EMC */
4647     case 0x94:  return EL_QUICKSAND_EMPTY;              /* EMC */
4648     case 0x9a:  return EL_AMOEBA_WET;                   /* EMC */
4649     case 0xaf:  return EL_DYNAMITE;                     /* EMC */
4650     case 0xbd:  return EL_SAND;                         /* EMC */
4651
4652     default:
4653       Error(ERR_WARN, "invalid level element %d", element);
4654       return EL_UNKNOWN;
4655   }
4656 }
4657
4658 static int map_em_element_field(int element)
4659 {
4660   if (element >= 0xc8 && element <= 0xe1)
4661     return EL_CHAR_A + (element - 0xc8);
4662   else if (element >= 0xe2 && element <= 0xeb)
4663     return EL_CHAR_0 + (element - 0xe2);
4664
4665   switch (element)
4666   {
4667     case 0x00:  return EL_ROCK;
4668     case 0x01:  return EL_ROCK;                         /* EMC */
4669     case 0x02:  return EL_DIAMOND;
4670     case 0x03:  return EL_DIAMOND;
4671     case 0x04:  return EL_ROBOT;
4672     case 0x05:  return EL_ROBOT;                        /* EMC */
4673     case 0x06:  return EL_EMPTY_SPACE;                  /* EMC */
4674     case 0x07:  return EL_EMPTY_SPACE;                  /* EMC */
4675     case 0x08:  return EL_SPACESHIP_UP;
4676     case 0x09:  return EL_SPACESHIP_RIGHT;
4677     case 0x0a:  return EL_SPACESHIP_DOWN;
4678     case 0x0b:  return EL_SPACESHIP_LEFT;
4679     case 0x0c:  return EL_SPACESHIP_UP;
4680     case 0x0d:  return EL_SPACESHIP_RIGHT;
4681     case 0x0e:  return EL_SPACESHIP_DOWN;
4682     case 0x0f:  return EL_SPACESHIP_LEFT;
4683
4684     case 0x10:  return EL_BOMB;
4685     case 0x11:  return EL_BOMB;                         /* EMC */
4686     case 0x12:  return EL_EMERALD;
4687     case 0x13:  return EL_EMERALD;
4688     case 0x14:  return EL_BUG_UP;
4689     case 0x15:  return EL_BUG_RIGHT;
4690     case 0x16:  return EL_BUG_DOWN;
4691     case 0x17:  return EL_BUG_LEFT;
4692     case 0x18:  return EL_BUG_UP;
4693     case 0x19:  return EL_BUG_RIGHT;
4694     case 0x1a:  return EL_BUG_DOWN;
4695     case 0x1b:  return EL_BUG_LEFT;
4696     case 0x1c:  return EL_AMOEBA_DROP;
4697     case 0x1d:  return EL_AMOEBA_DROP;                  /* EMC */
4698     case 0x1e:  return EL_AMOEBA_DROP;                  /* EMC */
4699     case 0x1f:  return EL_AMOEBA_DROP;                  /* EMC */
4700
4701     case 0x20:  return EL_ROCK;
4702     case 0x21:  return EL_BOMB;                         /* EMC */
4703     case 0x22:  return EL_DIAMOND;                      /* EMC */
4704     case 0x23:  return EL_EMERALD;                      /* EMC */
4705     case 0x24:  return EL_MAGIC_WALL;
4706     case 0x25:  return EL_NUT;
4707     case 0x26:  return EL_NUT;                          /* EMC */
4708     case 0x27:  return EL_NUT;                          /* EMC */
4709
4710       /* looks like magic wheel, but is _always_ activated */
4711     case 0x28:  return EL_ROBOT_WHEEL;                  /* EMC */
4712
4713     case 0x29:  return EL_YAMYAM;       /* up */
4714     case 0x2a:  return EL_YAMYAM;       /* down */
4715     case 0x2b:  return EL_YAMYAM;       /* left */      /* EMC */
4716     case 0x2c:  return EL_YAMYAM;       /* right */     /* EMC */
4717     case 0x2d:  return EL_QUICKSAND_FULL;
4718     case 0x2e:  return EL_EMPTY_SPACE;                  /* EMC */
4719     case 0x2f:  return EL_EMPTY_SPACE;                  /* EMC */
4720
4721     case 0x30:  return EL_EMPTY_SPACE;                  /* EMC */
4722     case 0x31:  return EL_SAND;                         /* EMC */
4723     case 0x32:  return EL_SAND;                         /* EMC */
4724     case 0x33:  return EL_SAND;                         /* EMC */
4725     case 0x34:  return EL_QUICKSAND_FULL;               /* EMC */
4726     case 0x35:  return EL_QUICKSAND_FULL;               /* EMC */
4727     case 0x36:  return EL_QUICKSAND_FULL;               /* EMC */
4728     case 0x37:  return EL_SAND;                         /* EMC */
4729     case 0x38:  return EL_ROCK;                         /* EMC */
4730     case 0x39:  return EL_EXPANDABLE_WALL_HORIZONTAL;   /* EMC */
4731     case 0x3a:  return EL_EXPANDABLE_WALL_VERTICAL;     /* EMC */
4732     case 0x3b:  return EL_DYNAMITE_ACTIVE;      /* 1 */
4733     case 0x3c:  return EL_DYNAMITE_ACTIVE;      /* 2 */
4734     case 0x3d:  return EL_DYNAMITE_ACTIVE;      /* 3 */
4735     case 0x3e:  return EL_DYNAMITE_ACTIVE;      /* 4 */
4736     case 0x3f:  return EL_ACID_POOL_BOTTOM;
4737
4738     case 0x40:  return EL_EXIT_OPEN;    /* 1 */
4739     case 0x41:  return EL_EXIT_OPEN;    /* 2 */
4740     case 0x42:  return EL_EXIT_OPEN;    /* 3 */
4741     case 0x43:  return EL_BALLOON;                      /* EMC */
4742     case 0x44:  return EL_UNKNOWN;                      /* EMC ("plant") */
4743     case 0x45:  return EL_SPRING;                       /* EMC */
4744     case 0x46:  return EL_SPRING;       /* falling */   /* EMC */
4745     case 0x47:  return EL_SPRING;       /* left */      /* EMC */
4746     case 0x48:  return EL_SPRING;       /* right */     /* EMC */
4747     case 0x49:  return EL_UNKNOWN;                      /* EMC ("ball 1") */
4748     case 0x4a:  return EL_UNKNOWN;                      /* EMC ("ball 2") */
4749     case 0x4b:  return EL_UNKNOWN;                      /* EMC ("android") */
4750     case 0x4c:  return EL_EMPTY_SPACE;                  /* EMC */
4751     case 0x4d:  return EL_UNKNOWN;                      /* EMC ("android") */
4752     case 0x4e:  return EL_INVISIBLE_WALL;               /* EMC (? "android") */
4753     case 0x4f:  return EL_UNKNOWN;                      /* EMC ("android") */
4754
4755     case 0x50:  return EL_UNKNOWN;                      /* EMC ("android") */
4756     case 0x51:  return EL_UNKNOWN;                      /* EMC ("android") */
4757     case 0x52:  return EL_UNKNOWN;                      /* EMC ("android") */
4758     case 0x53:  return EL_UNKNOWN;                      /* EMC ("android") */
4759     case 0x54:  return EL_UNKNOWN;                      /* EMC ("android") */
4760     case 0x55:  return EL_EMPTY_SPACE;                  /* EMC */
4761     case 0x56:  return EL_EMPTY_SPACE;                  /* EMC */
4762     case 0x57:  return EL_EMPTY_SPACE;                  /* EMC */
4763     case 0x58:  return EL_EMPTY_SPACE;                  /* EMC */
4764     case 0x59:  return EL_EMPTY_SPACE;                  /* EMC */
4765     case 0x5a:  return EL_EMPTY_SPACE;                  /* EMC */
4766     case 0x5b:  return EL_EMPTY_SPACE;                  /* EMC */
4767     case 0x5c:  return EL_EMPTY_SPACE;                  /* EMC */
4768     case 0x5d:  return EL_EMPTY_SPACE;                  /* EMC */
4769     case 0x5e:  return EL_EMPTY_SPACE;                  /* EMC */
4770     case 0x5f:  return EL_EMPTY_SPACE;                  /* EMC */
4771
4772     case 0x60:  return EL_EMPTY_SPACE;                  /* EMC */
4773     case 0x61:  return EL_EMPTY_SPACE;                  /* EMC */
4774     case 0x62:  return EL_EMPTY_SPACE;                  /* EMC */
4775     case 0x63:  return EL_SPRING;       /* left */      /* EMC */
4776     case 0x64:  return EL_SPRING;       /* right */     /* EMC */
4777     case 0x65:  return EL_ACID;         /* 1 */         /* EMC */
4778     case 0x66:  return EL_ACID;         /* 2 */         /* EMC */
4779     case 0x67:  return EL_ACID;         /* 3 */         /* EMC */
4780     case 0x68:  return EL_ACID;         /* 4 */         /* EMC */
4781     case 0x69:  return EL_ACID;         /* 5 */         /* EMC */
4782     case 0x6a:  return EL_ACID;         /* 6 */         /* EMC */
4783     case 0x6b:  return EL_ACID;         /* 7 */         /* EMC */
4784     case 0x6c:  return EL_ACID;         /* 8 */         /* EMC */
4785     case 0x6d:  return EL_EMPTY_SPACE;                  /* EMC */
4786     case 0x6e:  return EL_EMPTY_SPACE;                  /* EMC */
4787     case 0x6f:  return EL_EMPTY_SPACE;                  /* EMC */
4788
4789     case 0x70:  return EL_EMPTY_SPACE;                  /* EMC */
4790     case 0x71:  return EL_EMPTY_SPACE;                  /* EMC */
4791     case 0x72:  return EL_NUT;          /* left */      /* EMC */
4792     case 0x73:  return EL_SAND;                         /* EMC (? "nut") */
4793     case 0x74:  return EL_STEELWALL;
4794     case 0x75:  return EL_EMPTY_SPACE;                  /* EMC */
4795     case 0x76:  return EL_EMPTY_SPACE;                  /* EMC */
4796     case 0x77:  return EL_BOMB;         /* left */      /* EMC */
4797     case 0x78:  return EL_BOMB;         /* right */     /* EMC */
4798     case 0x79:  return EL_ROCK;         /* left */      /* EMC */
4799     case 0x7a:  return EL_ROCK;         /* right */     /* EMC */
4800     case 0x7b:  return EL_ACID;                         /* (? EMC "blank") */
4801     case 0x7c:  return EL_EMPTY_SPACE;                  /* EMC */
4802     case 0x7d:  return EL_EMPTY_SPACE;                  /* EMC */
4803     case 0x7e:  return EL_EMPTY_SPACE;                  /* EMC */
4804     case 0x7f:  return EL_EMPTY_SPACE;                  /* EMC */
4805
4806     case 0x80:  return EL_EMPTY;
4807     case 0x81:  return EL_WALL_SLIPPERY;
4808     case 0x82:  return EL_SAND;
4809     case 0x83:  return EL_STEELWALL;
4810     case 0x84:  return EL_WALL;
4811     case 0x85:  return EL_EM_KEY_1;
4812     case 0x86:  return EL_EM_KEY_2;
4813     case 0x87:  return EL_EM_KEY_4;
4814     case 0x88:  return EL_EM_KEY_3;
4815     case 0x89:  return EL_EM_GATE_1;
4816     case 0x8a:  return EL_EM_GATE_2;
4817     case 0x8b:  return EL_EM_GATE_4;
4818     case 0x8c:  return EL_EM_GATE_3;
4819     case 0x8d:  return EL_INVISIBLE_WALL;               /* EMC (? "dripper") */
4820     case 0x8e:  return EL_EM_GATE_1_GRAY;
4821     case 0x8f:  return EL_EM_GATE_2_GRAY;
4822
4823     case 0x90:  return EL_EM_GATE_4_GRAY;
4824     case 0x91:  return EL_EM_GATE_3_GRAY;
4825     case 0x92:  return EL_MAGIC_WALL;
4826     case 0x93:  return EL_ROBOT_WHEEL;
4827     case 0x94:  return EL_QUICKSAND_EMPTY;              /* (? EMC "sand") */
4828     case 0x95:  return EL_ACID_POOL_TOPLEFT;
4829     case 0x96:  return EL_ACID_POOL_TOPRIGHT;
4830     case 0x97:  return EL_ACID_POOL_BOTTOMLEFT;
4831     case 0x98:  return EL_ACID_POOL_BOTTOMRIGHT;
4832     case 0x99:  return EL_ACID;                 /* (? EMC "fake blank") */
4833     case 0x9a:  return EL_AMOEBA_DEAD;          /* 1 */
4834     case 0x9b:  return EL_AMOEBA_DEAD;          /* 2 */
4835     case 0x9c:  return EL_AMOEBA_DEAD;          /* 3 */
4836     case 0x9d:  return EL_AMOEBA_DEAD;          /* 4 */
4837     case 0x9e:  return EL_EXIT_CLOSED;
4838     case 0x9f:  return EL_CHAR_LESS;            /* arrow left */
4839
4840       /* looks like normal sand, but behaves like wall */
4841     case 0xa0:  return EL_UNKNOWN;              /* EMC ("fake grass") */
4842     case 0xa1:  return EL_UNKNOWN;              /* EMC ("lenses") */
4843     case 0xa2:  return EL_UNKNOWN;              /* EMC ("magnify") */
4844     case 0xa3:  return EL_UNKNOWN;              /* EMC ("fake blank") */
4845     case 0xa4:  return EL_UNKNOWN;              /* EMC ("fake grass") */
4846     case 0xa5:  return EL_UNKNOWN;              /* EMC ("switch") */
4847     case 0xa6:  return EL_UNKNOWN;              /* EMC ("switch") */
4848     case 0xa7:  return EL_EMPTY_SPACE;                  /* EMC */
4849     case 0xa8:  return EL_EMC_WALL_1;                   /* EMC ("decor 8") */
4850     case 0xa9:  return EL_EMC_WALL_2;                   /* EMC ("decor 9") */
4851     case 0xaa:  return EL_EMC_WALL_3;                   /* EMC ("decor 10") */
4852     case 0xab:  return EL_EMC_WALL_7;                   /* EMC ("decor 5") */
4853     case 0xac:  return EL_CHAR_COMMA;                   /* EMC */
4854     case 0xad:  return EL_CHAR_QUOTEDBL;                /* EMC */
4855     case 0xae:  return EL_CHAR_MINUS;                   /* EMC */
4856     case 0xaf:  return EL_DYNAMITE;
4857
4858     case 0xb0:  return EL_EMC_STEELWALL_1;              /* EMC ("steel 3") */
4859     case 0xb1:  return EL_EMC_WALL_8;                   /* EMC ("decor 6") */
4860     case 0xb2:  return EL_UNKNOWN;                      /* EMC ("decor 7") */
4861     case 0xb3:  return EL_STEELWALL;            /* 2 */ /* EMC */
4862     case 0xb4:  return EL_WALL_SLIPPERY;        /* 2 */ /* EMC */
4863     case 0xb5:  return EL_EMC_WALL_6;                   /* EMC ("decor 2") */
4864     case 0xb6:  return EL_EMC_WALL_5;                   /* EMC ("decor 4") */
4865     case 0xb7:  return EL_EMC_WALL_4;                   /* EMC ("decor 3") */
4866     case 0xb8:  return EL_BALLOON_SWITCH_ANY;           /* EMC */
4867     case 0xb9:  return EL_BALLOON_SWITCH_RIGHT;         /* EMC */
4868     case 0xba:  return EL_BALLOON_SWITCH_DOWN;          /* EMC */
4869     case 0xbb:  return EL_BALLOON_SWITCH_LEFT;          /* EMC */
4870     case 0xbc:  return EL_BALLOON_SWITCH_UP;            /* EMC */
4871     case 0xbd:  return EL_SAND;                         /* EMC ("dirt") */
4872     case 0xbe:  return EL_UNKNOWN;                      /* EMC ("plant") */
4873     case 0xbf:  return EL_UNKNOWN;                      /* EMC ("key 5") */
4874
4875     case 0xc0:  return EL_UNKNOWN;                      /* EMC ("key 6") */
4876     case 0xc1:  return EL_UNKNOWN;                      /* EMC ("key 7") */
4877     case 0xc2:  return EL_UNKNOWN;                      /* EMC ("key 8") */
4878     case 0xc3:  return EL_UNKNOWN;                      /* EMC ("door 5") */
4879     case 0xc4:  return EL_UNKNOWN;                      /* EMC ("door 6") */
4880     case 0xc5:  return EL_UNKNOWN;                      /* EMC ("door 7") */
4881     case 0xc6:  return EL_UNKNOWN;                      /* EMC ("door 8") */
4882     case 0xc7:  return EL_UNKNOWN;                      /* EMC ("bumper") */
4883
4884       /* characters: see above */
4885
4886     case 0xec:  return EL_CHAR_PERIOD;
4887     case 0xed:  return EL_CHAR_EXCLAM;
4888     case 0xee:  return EL_CHAR_COLON;
4889     case 0xef:  return EL_CHAR_QUESTION;
4890
4891     case 0xf0:  return EL_CHAR_GREATER;                 /* arrow right */
4892     case 0xf1:  return EL_CHAR_COPYRIGHT;               /* EMC: "decor 1" */
4893     case 0xf2:  return EL_UNKNOWN;              /* EMC ("fake door 5") */
4894     case 0xf3:  return EL_UNKNOWN;              /* EMC ("fake door 6") */
4895     case 0xf4:  return EL_UNKNOWN;              /* EMC ("fake door 7") */
4896     case 0xf5:  return EL_UNKNOWN;              /* EMC ("fake door 8") */
4897     case 0xf6:  return EL_EMPTY_SPACE;                  /* EMC */
4898     case 0xf7:  return EL_EMPTY_SPACE;                  /* EMC */
4899
4900     case 0xf8:  return EL_EMPTY_SPACE;                  /* EMC */
4901     case 0xf9:  return EL_EMPTY_SPACE;                  /* EMC */
4902     case 0xfa:  return EL_EMPTY_SPACE;                  /* EMC */
4903     case 0xfb:  return EL_EMPTY_SPACE;                  /* EMC */
4904     case 0xfc:  return EL_EMPTY_SPACE;                  /* EMC */
4905     case 0xfd:  return EL_EMPTY_SPACE;                  /* EMC */
4906
4907     case 0xfe:  return EL_PLAYER_1;                     /* EMC: "blank" */
4908     case 0xff:  return EL_PLAYER_2;                     /* EMC: "blank" */
4909
4910     default:
4911       /* should never happen (all 8-bit value cases should be handled) */
4912       Error(ERR_WARN, "invalid level element %d", element);
4913       return EL_UNKNOWN;
4914   }
4915 }
4916
4917 #define EM_LEVEL_SIZE                   2106
4918 #define EM_LEVEL_XSIZE                  64
4919 #define EM_LEVEL_YSIZE                  32
4920
4921 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
4922                                          struct LevelFileInfo *level_file_info)
4923 {
4924   char *filename = level_file_info->filename;
4925   FILE *file;
4926   unsigned char leveldata[EM_LEVEL_SIZE];
4927   unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
4928   int nr = level_file_info->nr;
4929   int i, x, y;
4930
4931   if (!(file = fopen(filename, MODE_READ)))
4932   {
4933     level->no_valid_file = TRUE;
4934
4935     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4936
4937     return;
4938   }
4939
4940   for (i = 0; i < EM_LEVEL_SIZE; i++)
4941     leveldata[i] = fgetc(file);
4942
4943   fclose(file);
4944
4945   /* check if level data is crypted by testing against known starting bytes
4946      of the few existing crypted level files (from Emerald Mine 1 + 2) */
4947
4948   if ((leveldata[0] == 0xf1 ||
4949        leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
4950   {
4951     unsigned char code0 = 0x65;
4952     unsigned char code1 = 0x11;
4953
4954     if (leveldata[0] == 0xf5)   /* error in crypted Emerald Mine 2 levels */
4955       leveldata[0] = 0xf1;
4956
4957     /* decode crypted level data */
4958
4959     for (i = 0; i < EM_LEVEL_SIZE; i++)
4960     {
4961       leveldata[i] ^= code0;
4962       leveldata[i] -= code1;
4963
4964       code0 = (code0 + 7) & 0xff;
4965     }
4966   }
4967
4968   level->fieldx = EM_LEVEL_XSIZE;
4969   level->fieldy = EM_LEVEL_YSIZE;
4970
4971   level->time           = header[46] * 10;
4972   level->gems_needed    = header[47];
4973
4974   /* The original Emerald Mine levels have their level number stored
4975      at the second byte of the level file...
4976      Do not trust this information at other level files, e.g. EMC,
4977      but correct it anyway (normally the first row is completely
4978      steel wall, so the correction does not hurt anyway). */
4979
4980   if (leveldata[1] == nr)
4981     leveldata[1] = leveldata[2];        /* correct level number field */
4982
4983   sprintf(level->name, "Level %d", nr);         /* set level name */
4984
4985   level->score[SC_EMERALD]      = header[36];
4986   level->score[SC_DIAMOND]      = header[37];
4987   level->score[SC_ROBOT]        = header[38];
4988   level->score[SC_SPACESHIP]    = header[39];
4989   level->score[SC_BUG]          = header[40];
4990   level->score[SC_YAMYAM]       = header[41];
4991   level->score[SC_NUT]          = header[42];
4992   level->score[SC_DYNAMITE]     = header[43];
4993   level->score[SC_TIME_BONUS]   = header[44];
4994
4995   level->num_yamyam_contents = 4;
4996
4997   for (i = 0; i < level->num_yamyam_contents; i++)
4998     for (y = 0; y < 3; y++)
4999       for (x = 0; x < 3; x++)
5000         level->yamyam_content[i].e[x][y] =
5001           map_em_element_yam(header[i * 9 + y * 3 + x]);
5002
5003   level->amoeba_speed           = (header[52] * 256 + header[53]) % 256;
5004   level->time_magic_wall        = (header[54] * 256 + header[55]) * 16 / 100;
5005   level->time_wheel             = (header[56] * 256 + header[57]) * 16 / 100;
5006   level->amoeba_content         = EL_DIAMOND;
5007
5008   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5009   {
5010     int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
5011
5012     if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
5013       new_element = EL_AMOEBA_WET;
5014
5015     level->field[x][y] = new_element;
5016   }
5017
5018   x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
5019   y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
5020   level->field[x][y] = EL_PLAYER_1;
5021
5022   x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
5023   y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
5024   level->field[x][y] = EL_PLAYER_2;
5025 }
5026
5027 #endif
5028
5029 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
5030 {
5031   static int ball_xy[8][2] =
5032   {
5033     { 0, 0 },
5034     { 1, 0 },
5035     { 2, 0 },
5036     { 0, 1 },
5037     { 2, 1 },
5038     { 0, 2 },
5039     { 1, 2 },
5040     { 2, 2 },
5041   };
5042   struct LevelInfo_EM *level_em = level->native_em_level;
5043   struct LEVEL *lev = level_em->lev;
5044   struct PLAYER **ply = level_em->ply;
5045   int i, j, x, y;
5046
5047   lev->width  = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
5048   lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
5049
5050   lev->time_seconds     = level->time;
5051   lev->required_initial = level->gems_needed;
5052
5053   lev->emerald_score    = level->score[SC_EMERALD];
5054   lev->diamond_score    = level->score[SC_DIAMOND];
5055   lev->alien_score      = level->score[SC_ROBOT];
5056   lev->tank_score       = level->score[SC_SPACESHIP];
5057   lev->bug_score        = level->score[SC_BUG];
5058   lev->eater_score      = level->score[SC_YAMYAM];
5059   lev->nut_score        = level->score[SC_NUT];
5060   lev->dynamite_score   = level->score[SC_DYNAMITE];
5061   lev->key_score        = level->score[SC_KEY];
5062   lev->exit_score       = level->score[SC_TIME_BONUS];
5063
5064   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
5065     for (y = 0; y < 3; y++)
5066       for (x = 0; x < 3; x++)
5067         lev->eater_array[i][y * 3 + x] =
5068           map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
5069
5070   lev->amoeba_time              = level->amoeba_speed;
5071   lev->wonderwall_time_initial  = level->time_magic_wall;
5072   lev->wheel_time               = level->time_wheel;
5073
5074   lev->android_move_time        = level->android_move_time;
5075   lev->android_clone_time       = level->android_clone_time;
5076   lev->ball_random              = level->ball_random;
5077   lev->ball_state_initial       = level->ball_state_initial;
5078   lev->ball_time                = level->ball_time;
5079   lev->num_ball_arrays          = level->num_ball_contents;
5080
5081   lev->lenses_score             = level->lenses_score;
5082   lev->magnify_score            = level->magnify_score;
5083   lev->slurp_score              = level->slurp_score;
5084
5085   lev->lenses_time              = level->lenses_time;
5086   lev->magnify_time             = level->magnify_time;
5087
5088   lev->wind_direction_initial =
5089     map_direction_RND_to_EM(level->wind_direction_initial);
5090   lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
5091                            lev->wind_time : 0);
5092
5093   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
5094     for (j = 0; j < 8; j++)
5095       lev->ball_array[i][j] =
5096         map_element_RND_to_EM(level->
5097                               ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
5098
5099   map_android_clone_elements_RND_to_EM(level);
5100
5101   /* first fill the complete playfield with the default border element */
5102   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
5103     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
5104       level_em->cave[x][y] = ZBORDER;
5105
5106   if (BorderElement == EL_STEELWALL)
5107   {
5108     for (y = 0; y < lev->height + 2; y++)
5109       for (x = 0; x < lev->width + 2; x++)
5110         level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
5111   }
5112
5113   /* then copy the real level contents from level file into the playfield */
5114   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
5115   {
5116     int new_element = map_element_RND_to_EM(level->field[x][y]);
5117     int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
5118     int xx = x + 1 + offset;
5119     int yy = y + 1 + offset;
5120
5121     if (level->field[x][y] == EL_AMOEBA_DEAD)
5122       new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
5123
5124     level_em->cave[xx][yy] = new_element;
5125   }
5126
5127   for (i = 0; i < MAX_PLAYERS; i++)
5128   {
5129     ply[i]->x_initial = 0;
5130     ply[i]->y_initial = 0;
5131   }
5132
5133   /* initialize player positions and delete players from the playfield */
5134   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
5135   {
5136     if (ELEM_IS_PLAYER(level->field[x][y]))
5137     {
5138       int player_nr = GET_PLAYER_NR(level->field[x][y]);
5139       int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
5140       int xx = x + 1 + offset;
5141       int yy = y + 1 + offset;
5142
5143       ply[player_nr]->x_initial = xx;
5144       ply[player_nr]->y_initial = yy;
5145
5146       level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
5147     }
5148   }
5149
5150   if (BorderElement == EL_STEELWALL)
5151   {
5152     lev->width  += 2;
5153     lev->height += 2;
5154   }
5155 }
5156
5157 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
5158 {
5159   static int ball_xy[8][2] =
5160   {
5161     { 0, 0 },
5162     { 1, 0 },
5163     { 2, 0 },
5164     { 0, 1 },
5165     { 2, 1 },
5166     { 0, 2 },
5167     { 1, 2 },
5168     { 2, 2 },
5169   };
5170   struct LevelInfo_EM *level_em = level->native_em_level;
5171   struct LEVEL *lev = level_em->lev;
5172   struct PLAYER **ply = level_em->ply;
5173   int i, j, x, y;
5174
5175   level->fieldx = MIN(lev->width,  MAX_LEV_FIELDX);
5176   level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
5177
5178   level->time        = lev->time_seconds;
5179   level->gems_needed = lev->required_initial;
5180
5181   sprintf(level->name, "Level %d", level->file_info.nr);
5182
5183   level->score[SC_EMERALD]      = lev->emerald_score;
5184   level->score[SC_DIAMOND]      = lev->diamond_score;
5185   level->score[SC_ROBOT]        = lev->alien_score;
5186   level->score[SC_SPACESHIP]    = lev->tank_score;
5187   level->score[SC_BUG]          = lev->bug_score;
5188   level->score[SC_YAMYAM]       = lev->eater_score;
5189   level->score[SC_NUT]          = lev->nut_score;
5190   level->score[SC_DYNAMITE]     = lev->dynamite_score;
5191   level->score[SC_KEY]          = lev->key_score;
5192   level->score[SC_TIME_BONUS]   = lev->exit_score;
5193
5194   level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
5195
5196   for (i = 0; i < level->num_yamyam_contents; i++)
5197     for (y = 0; y < 3; y++)
5198       for (x = 0; x < 3; x++)
5199         level->yamyam_content[i].e[x][y] =
5200           map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
5201
5202   level->amoeba_speed           = lev->amoeba_time;
5203   level->time_magic_wall        = lev->wonderwall_time_initial;
5204   level->time_wheel             = lev->wheel_time;
5205
5206   level->android_move_time      = lev->android_move_time;
5207   level->android_clone_time     = lev->android_clone_time;
5208   level->ball_random            = lev->ball_random;
5209   level->ball_state_initial     = lev->ball_state_initial;
5210   level->ball_time              = lev->ball_time;
5211   level->num_ball_contents      = lev->num_ball_arrays;
5212
5213   level->lenses_score           = lev->lenses_score;
5214   level->magnify_score          = lev->magnify_score;
5215   level->slurp_score            = lev->slurp_score;
5216
5217   level->lenses_time            = lev->lenses_time;
5218   level->magnify_time           = lev->magnify_time;
5219
5220   level->wind_direction_initial =
5221     map_direction_EM_to_RND(lev->wind_direction_initial);
5222
5223   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
5224     for (j = 0; j < 8; j++)
5225       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
5226         map_element_EM_to_RND(lev->ball_array[i][j]);
5227
5228   map_android_clone_elements_EM_to_RND(level);
5229
5230   /* convert the playfield (some elements need special treatment) */
5231   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5232   {
5233     int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
5234
5235     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
5236       new_element = EL_AMOEBA_DEAD;
5237
5238     level->field[x][y] = new_element;
5239   }
5240
5241   for (i = 0; i < MAX_PLAYERS; i++)
5242   {
5243     /* in case of all players set to the same field, use the first player */
5244     int nr = MAX_PLAYERS - i - 1;
5245     int jx = ply[nr]->x_initial - 1;
5246     int jy = ply[nr]->y_initial - 1;
5247
5248     if (jx != -1 && jy != -1)
5249       level->field[jx][jy] = EL_PLAYER_1 + nr;
5250   }
5251 }
5252
5253
5254 /* ------------------------------------------------------------------------- */
5255 /* functions for loading SP level                                            */
5256 /* ------------------------------------------------------------------------- */
5257
5258 #if 0
5259
5260 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
5261 #define SP_LEVEL_SIZE                   1536
5262 #define SP_LEVEL_XSIZE                  60
5263 #define SP_LEVEL_YSIZE                  24
5264 #define SP_LEVEL_NAME_LEN               23
5265
5266 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
5267                                        int nr)
5268 {
5269   int initial_player_gravity;
5270   int num_special_ports;
5271   int i, x, y;
5272
5273   /* for details of the Supaplex level format, see Herman Perk's Supaplex
5274      documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
5275
5276   /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
5277   for (y = 0; y < SP_LEVEL_YSIZE; y++)
5278   {
5279     for (x = 0; x < SP_LEVEL_XSIZE; x++)
5280     {
5281       int element_old = fgetc(file);
5282       int element_new;
5283
5284       if (element_old <= 0x27)
5285         element_new = getMappedElement(EL_SP_START + element_old);
5286       else if (element_old == 0x28)
5287         element_new = EL_INVISIBLE_WALL;
5288       else
5289       {
5290         Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
5291         Error(ERR_WARN, "invalid level element %d", element_old);
5292
5293         element_new = EL_UNKNOWN;
5294       }
5295
5296       level->field[x][y] = element_new;
5297     }
5298   }
5299
5300   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
5301
5302   /* initial gravity: 1 == "on", anything else (0) == "off" */
5303   initial_player_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
5304
5305   for (i = 0; i < MAX_PLAYERS; i++)
5306     level->initial_player_gravity[i] = initial_player_gravity;
5307
5308   ReadUnusedBytesFromFile(file, 1);     /* (not used by Supaplex engine) */
5309
5310   /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
5311   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5312     level->name[i] = fgetc(file);
5313   level->name[SP_LEVEL_NAME_LEN] = '\0';
5314
5315   /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
5316   ReadUnusedBytesFromFile(file, 1);     /* (not used by R'n'D engine) */
5317
5318   /* number of infotrons needed; 0 means that Supaplex will count the total
5319      amount of infotrons in the level and use the low byte of that number
5320      (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
5321   level->gems_needed = fgetc(file);
5322
5323   /* number of special ("gravity") port entries below (maximum 10 allowed) */
5324   num_special_ports = fgetc(file);
5325
5326   /* database of properties of up to 10 special ports (6 bytes per port) */
5327   for (i = 0; i < 10; i++)
5328   {
5329     int port_location, port_x, port_y, port_element;
5330     int gravity;
5331
5332     /* high and low byte of the location of a special port; if (x, y) are the
5333        coordinates of a port in the field and (0, 0) is the top-left corner,
5334        the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
5335        of what may be expected: Supaplex works with a game field in memory
5336        which is 2 bytes per tile) */
5337     port_location = getFile16BitBE(file);
5338
5339     /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
5340     gravity = fgetc(file);
5341
5342     /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
5343     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
5344
5345     /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
5346     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
5347
5348     ReadUnusedBytesFromFile(file, 1);   /* (not used by Supaplex engine) */
5349
5350     if (i >= num_special_ports)
5351       continue;
5352
5353     port_x = (port_location / 2) % SP_LEVEL_XSIZE;
5354     port_y = (port_location / 2) / SP_LEVEL_XSIZE;
5355
5356     if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
5357         port_y < 0 || port_y >= SP_LEVEL_YSIZE)
5358     {
5359       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
5360             port_x, port_y);
5361
5362       continue;
5363     }
5364
5365     port_element = level->field[port_x][port_y];
5366
5367     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5368         port_element > EL_SP_GRAVITY_PORT_UP)
5369     {
5370       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
5371
5372       continue;
5373     }
5374
5375     /* change previous (wrong) gravity inverting special port to either
5376        gravity enabling special port or gravity disabling special port */
5377     level->field[port_x][port_y] +=
5378       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5379        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5380   }
5381
5382   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
5383
5384   /* change special gravity ports without database entries to normal ports */
5385   for (y = 0; y < SP_LEVEL_YSIZE; y++)
5386     for (x = 0; x < SP_LEVEL_XSIZE; x++)
5387       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5388           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5389         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5390
5391   /* auto-determine number of infotrons if it was stored as "0" -- see above */
5392   if (level->gems_needed == 0)
5393   {
5394     for (y = 0; y < SP_LEVEL_YSIZE; y++)
5395       for (x = 0; x < SP_LEVEL_XSIZE; x++)
5396         if (level->field[x][y] == EL_SP_INFOTRON)
5397           level->gems_needed++;
5398
5399     level->gems_needed &= 0xff;         /* only use low byte -- see above */
5400   }
5401
5402   level->fieldx = SP_LEVEL_XSIZE;
5403   level->fieldy = SP_LEVEL_YSIZE;
5404
5405   level->time = 0;                      /* no time limit */
5406   level->amoeba_speed = 0;
5407   level->time_magic_wall = 0;
5408   level->time_wheel = 0;
5409   level->amoeba_content = EL_EMPTY;
5410
5411 #if 1
5412   /* original Supaplex does not use score values -- use default values */
5413 #else
5414   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5415     level->score[i] = 0;
5416 #endif
5417
5418   /* there are no yamyams in supaplex levels */
5419   for (i = 0; i < level->num_yamyam_contents; i++)
5420     for (y = 0; y < 3; y++)
5421       for (x = 0; x < 3; x++)
5422         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5423 }
5424
5425 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5426                                      struct LevelFileInfo *level_file_info,
5427                                      boolean level_info_only)
5428 {
5429   char *filename = level_file_info->filename;
5430   FILE *file;
5431   int nr = level_file_info->nr - leveldir_current->first_level;
5432   int i, l, x, y;
5433   char name_first, name_last;
5434   struct LevelInfo multipart_level;
5435   int multipart_xpos, multipart_ypos;
5436   boolean is_multipart_level;
5437   boolean is_first_part;
5438   boolean reading_multipart_level = FALSE;
5439   boolean use_empty_level = FALSE;
5440
5441   if (!(file = fopen(filename, MODE_READ)))
5442   {
5443     level->no_valid_file = TRUE;
5444
5445     if (!level_info_only)
5446       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5447
5448     return;
5449   }
5450
5451   /* position file stream to the requested level inside the level package */
5452   if (level_file_info->packed &&
5453       fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
5454   {
5455     level->no_valid_file = TRUE;
5456
5457     Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
5458
5459     return;
5460   }
5461
5462   /* there exist Supaplex level package files with multi-part levels which
5463      can be detected as follows: instead of leading and trailing dashes ('-')
5464      to pad the level name, they have leading and trailing numbers which are
5465      the x and y coordinations of the current part of the multi-part level;
5466      if there are '?' characters instead of numbers on the left or right side
5467      of the level name, the multi-part level consists of only horizontal or
5468      vertical parts */
5469
5470   for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
5471   {
5472     LoadLevelFromFileStream_SP(file, level, l);
5473
5474     /* check if this level is a part of a bigger multi-part level */
5475
5476     name_first = level->name[0];
5477     name_last  = level->name[SP_LEVEL_NAME_LEN - 1];
5478
5479     is_multipart_level =
5480       ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
5481        (name_last  == '?' || (name_last  >= '0' && name_last  <= '9')));
5482
5483     is_first_part =
5484       ((name_first == '?' || name_first == '1') &&
5485        (name_last  == '?' || name_last  == '1'));
5486
5487     /* correct leading multipart level meta information in level name */
5488     for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
5489       level->name[i] = '-';
5490
5491     /* correct trailing multipart level meta information in level name */
5492     for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && level->name[i] == name_last; i--)
5493       level->name[i] = '-';
5494
5495     /* ---------- check for normal single level ---------- */
5496
5497     if (!reading_multipart_level && !is_multipart_level)
5498     {
5499       /* the current level is simply a normal single-part level, and we are
5500          not reading a multi-part level yet, so return the level as it is */
5501
5502       break;
5503     }
5504
5505     /* ---------- check for empty level (unused multi-part) ---------- */
5506
5507     if (!reading_multipart_level && is_multipart_level && !is_first_part)
5508     {
5509       /* this is a part of a multi-part level, but not the first part
5510          (and we are not already reading parts of a multi-part level);
5511          in this case, use an empty level instead of the single part */
5512
5513       use_empty_level = TRUE;
5514
5515       break;
5516     }
5517
5518     /* ---------- check for finished multi-part level ---------- */
5519
5520     if (reading_multipart_level &&
5521         (!is_multipart_level ||
5522          !strEqual(level->name, multipart_level.name)))
5523     {
5524       /* we are already reading parts of a multi-part level, but this level is
5525          either not a multi-part level, or a part of a different multi-part
5526          level; in both cases, the multi-part level seems to be complete */
5527
5528       break;
5529     }
5530
5531     /* ---------- here we have one part of a multi-part level ---------- */
5532
5533     reading_multipart_level = TRUE;
5534
5535     if (is_first_part)  /* start with first part of new multi-part level */
5536     {
5537       /* copy level info structure from first part */
5538       multipart_level = *level;
5539
5540       /* clear playfield of new multi-part level */
5541       for (y = 0; y < MAX_LEV_FIELDY; y++)
5542         for (x = 0; x < MAX_LEV_FIELDX; x++)
5543           multipart_level.field[x][y] = EL_EMPTY;
5544     }
5545
5546     if (name_first == '?')
5547       name_first = '1';
5548     if (name_last == '?')
5549       name_last = '1';
5550
5551     multipart_xpos = (int)(name_first - '0');
5552     multipart_ypos = (int)(name_last  - '0');
5553
5554 #if 0
5555     printf("----------> part (%d/%d) of multi-part level '%s'\n",
5556            multipart_xpos, multipart_ypos, multipart_level.name);
5557 #endif
5558
5559     if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
5560         multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
5561     {
5562       Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
5563
5564       break;
5565     }
5566
5567     multipart_level.fieldx = MAX(multipart_level.fieldx,
5568                                  multipart_xpos * SP_LEVEL_XSIZE);
5569     multipart_level.fieldy = MAX(multipart_level.fieldy,
5570                                  multipart_ypos * SP_LEVEL_YSIZE);
5571
5572     /* copy level part at the right position of multi-part level */
5573     for (y = 0; y < SP_LEVEL_YSIZE; y++)
5574     {
5575       for (x = 0; x < SP_LEVEL_XSIZE; x++)
5576       {
5577         int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
5578         int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
5579
5580         multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
5581       }
5582     }
5583   }
5584
5585   fclose(file);
5586
5587   if (use_empty_level)
5588   {
5589     setLevelInfoToDefaults(level);
5590
5591     level->fieldx = SP_LEVEL_XSIZE;
5592     level->fieldy = SP_LEVEL_YSIZE;
5593
5594     for (y = 0; y < SP_LEVEL_YSIZE; y++)
5595       for (x = 0; x < SP_LEVEL_XSIZE; x++)
5596         level->field[x][y] = EL_EMPTY;
5597
5598     strcpy(level->name, "-------- EMPTY --------");
5599
5600     Error(ERR_WARN, "single part of multi-part level -- using empty level");
5601   }
5602
5603   if (reading_multipart_level)
5604     *level = multipart_level;
5605 }
5606
5607 #endif
5608
5609 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
5610 {
5611   struct LevelInfo_SP *level_sp = level->native_sp_level;
5612   LevelInfoType *header = &level_sp->header;
5613   int i, x, y;
5614
5615   level_sp->width  = level->fieldx;
5616   level_sp->height = level->fieldy;
5617
5618   for (x = 0; x < level->fieldx; x++)
5619     for (y = 0; y < level->fieldy; y++)
5620       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
5621
5622   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
5623
5624   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5625     header->LevelTitle[i] = level->name[i];
5626   /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
5627
5628   header->InfotronsNeeded = level->gems_needed;
5629
5630   header->SpecialPortCount = 0;
5631
5632   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
5633   {
5634     boolean gravity_port_found = FALSE;
5635     boolean gravity_port_valid = FALSE;
5636     int gravity_port_flag;
5637     int gravity_port_base_element;
5638     int element = level->field[x][y];
5639
5640     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
5641         element <= EL_SP_GRAVITY_ON_PORT_UP)
5642     {
5643       gravity_port_found = TRUE;
5644       gravity_port_valid = TRUE;
5645       gravity_port_flag = 1;
5646       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
5647     }
5648     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
5649              element <= EL_SP_GRAVITY_OFF_PORT_UP)
5650     {
5651       gravity_port_found = TRUE;
5652       gravity_port_valid = TRUE;
5653       gravity_port_flag = 0;
5654       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
5655     }
5656     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
5657              element <= EL_SP_GRAVITY_PORT_UP)
5658     {
5659       /* change R'n'D style gravity inverting special port to normal port
5660          (there are no gravity inverting ports in native Supaplex engine) */
5661
5662       gravity_port_found = TRUE;
5663       gravity_port_valid = FALSE;
5664       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
5665     }
5666
5667     if (gravity_port_found)
5668     {
5669       if (gravity_port_valid &&
5670           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5671       {
5672         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5673
5674         port->PortLocation = (y * level->fieldx + x) * 2;
5675         port->Gravity = gravity_port_flag;
5676
5677         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5678
5679         header->SpecialPortCount++;
5680       }
5681       else
5682       {
5683         /* change special gravity port to normal port */
5684
5685         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5686       }
5687
5688       level_sp->playfield[x][y] = element - EL_SP_START;
5689     }
5690   }
5691 }
5692
5693 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5694 {
5695   struct LevelInfo_SP *level_sp = level->native_sp_level;
5696   LevelInfoType *header = &level_sp->header;
5697   int i, x, y;
5698
5699   level->fieldx = level_sp->width;
5700   level->fieldy = level_sp->height;
5701
5702   for (x = 0; x < level->fieldx; x++)
5703   {
5704     for (y = 0; y < level->fieldy; y++)
5705     {
5706       int element_old = level_sp->playfield[x][y];
5707       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5708
5709       if (element_new == EL_UNKNOWN)
5710         Error(ERR_WARN, "invalid element %d at position %d, %d",
5711               element_old, x, y);
5712
5713       level->field[x][y] = element_new;
5714     }
5715   }
5716
5717   for (i = 0; i < MAX_PLAYERS; i++)
5718     level->initial_player_gravity[i] =
5719       (header->InitialGravity == 1 ? TRUE : FALSE);
5720
5721   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5722     level->name[i] = header->LevelTitle[i];
5723   level->name[SP_LEVEL_NAME_LEN] = '\0';
5724
5725   level->gems_needed = header->InfotronsNeeded;
5726
5727   for (i = 0; i < header->SpecialPortCount; i++)
5728   {
5729     SpecialPortType *port = &header->SpecialPort[i];
5730     int port_location = port->PortLocation;
5731     int gravity = port->Gravity;
5732     int port_x, port_y, port_element;
5733
5734     port_x = (port_location / 2) % level->fieldx;
5735     port_y = (port_location / 2) / level->fieldx;
5736
5737     if (port_x < 0 || port_x >= level->fieldx ||
5738         port_y < 0 || port_y >= level->fieldy)
5739     {
5740       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
5741             port_x, port_y);
5742
5743       continue;
5744     }
5745
5746     port_element = level->field[port_x][port_y];
5747
5748     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5749         port_element > EL_SP_GRAVITY_PORT_UP)
5750     {
5751       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
5752
5753       continue;
5754     }
5755
5756     /* change previous (wrong) gravity inverting special port to either
5757        gravity enabling special port or gravity disabling special port */
5758     level->field[port_x][port_y] +=
5759       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5760        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5761   }
5762
5763   /* change special gravity ports without database entries to normal ports */
5764   for (x = 0; x < level->fieldx; x++)
5765     for (y = 0; y < level->fieldy; y++)
5766       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5767           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5768         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5769
5770   level->time = 0;                      /* no time limit */
5771   level->amoeba_speed = 0;
5772   level->time_magic_wall = 0;
5773   level->time_wheel = 0;
5774   level->amoeba_content = EL_EMPTY;
5775
5776 #if 1
5777   /* original Supaplex does not use score values -- use default values */
5778 #else
5779   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5780     level->score[i] = 0;
5781 #endif
5782
5783   /* there are no yamyams in supaplex levels */
5784   for (i = 0; i < level->num_yamyam_contents; i++)
5785     for (x = 0; x < 3; x++)
5786       for (y = 0; y < 3; y++)
5787         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5788 }
5789
5790 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5791 {
5792   struct LevelInfo_SP *level_sp = level->native_sp_level;
5793   struct DemoInfo_SP *demo = &level_sp->demo;
5794   int i, j;
5795
5796   /* always start with reliable default values */
5797   demo->is_available = FALSE;
5798   demo->length = 0;
5799
5800   if (TAPE_IS_EMPTY(tape))
5801     return;
5802
5803   demo->level_nr = tape.level_nr;       /* (currently not used) */
5804
5805   level_sp->header.DemoRandomSeed = tape.random_seed;
5806
5807   demo->length = 0;
5808   for (i = 0; i < tape.length; i++)
5809   {
5810     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5811     int demo_repeat = tape.pos[i].delay;
5812
5813     for (j = 0; j < demo_repeat / 16; j++)
5814       demo->data[demo->length++] = 0xf0 | demo_action;
5815
5816     if (demo_repeat % 16)
5817       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5818   }
5819
5820   demo->data[demo->length++] = 0xff;
5821
5822   demo->is_available = TRUE;
5823 }
5824
5825 static void setTapeInfoToDefaults();
5826
5827 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5828 {
5829   struct LevelInfo_SP *level_sp = level->native_sp_level;
5830   struct DemoInfo_SP *demo = &level_sp->demo;
5831   char *filename = level->file_info.filename;
5832   int i;
5833
5834   /* always start with reliable default values */
5835   setTapeInfoToDefaults();
5836
5837   if (!demo->is_available)
5838     return;
5839
5840   tape.level_nr = demo->level_nr;       /* (currently not used) */
5841   tape.length = demo->length - 1;       /* without "end of demo" byte */
5842   tape.random_seed = level_sp->header.DemoRandomSeed;
5843
5844   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5845
5846   for (i = 0; i < demo->length - 1; i++)
5847   {
5848     int demo_action = demo->data[i] & 0x0f;
5849     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5850
5851     tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
5852     tape.pos[i].delay = demo_repeat + 1;
5853   }
5854
5855   tape.length_seconds = GetTapeLength();
5856 }
5857
5858
5859 /* ------------------------------------------------------------------------- */
5860 /* functions for loading DC level                                            */
5861 /* ------------------------------------------------------------------------- */
5862
5863 #define DC_LEVEL_HEADER_SIZE            344
5864
5865 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
5866 {
5867   static int last_data_encoded;
5868   static int offset1;
5869   static int offset2;
5870   int diff;
5871   int diff_hi, diff_lo;
5872   int data_hi, data_lo;
5873   unsigned short data_decoded;
5874
5875   if (init)
5876   {
5877     last_data_encoded = 0;
5878     offset1 = -1;
5879     offset2 = 0;
5880
5881     return 0;
5882   }
5883
5884   diff = data_encoded - last_data_encoded;
5885   diff_hi = diff & ~0xff;
5886   diff_lo = diff &  0xff;
5887
5888   offset2 += diff_lo;
5889
5890   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5891   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5892   data_hi = data_hi & 0xff00;
5893
5894   data_decoded = data_hi | data_lo;
5895
5896   last_data_encoded = data_encoded;
5897
5898   offset1 = (offset1 + 1) % 31;
5899   offset2 = offset2 & 0xff;
5900
5901   return data_decoded;
5902 }
5903
5904 int getMappedElement_DC(int element)
5905 {
5906   switch (element)
5907   {
5908     case 0x0000:
5909       element = EL_ROCK;
5910       break;
5911
5912       /* 0x0117 - 0x036e: (?) */
5913       /* EL_DIAMOND */
5914
5915       /* 0x042d - 0x0684: (?) */
5916       /* EL_EMERALD */
5917
5918     case 0x06f1:
5919       element = EL_NUT;
5920       break;
5921
5922     case 0x074c:
5923       element = EL_BOMB;
5924       break;
5925
5926     case 0x07a4:
5927       element = EL_PEARL;
5928       break;
5929
5930     case 0x0823:
5931       element = EL_CRYSTAL;
5932       break;
5933
5934     case 0x0e77:        /* quicksand (boulder) */
5935       element = EL_QUICKSAND_FAST_FULL;
5936       break;
5937
5938     case 0x0e99:        /* slow quicksand (boulder) */
5939       element = EL_QUICKSAND_FULL;
5940       break;
5941
5942     case 0x0ed2:
5943       element = EL_EM_EXIT_OPEN;
5944       break;
5945
5946     case 0x0ee3:
5947       element = EL_EM_EXIT_CLOSED;
5948       break;
5949
5950     case 0x0eeb:
5951       element = EL_EM_STEEL_EXIT_OPEN;
5952       break;
5953
5954     case 0x0efc:
5955       element = EL_EM_STEEL_EXIT_CLOSED;
5956       break;
5957
5958     case 0x0f4f:        /* dynamite (lit 1) */
5959       element = EL_EM_DYNAMITE_ACTIVE;
5960       break;
5961
5962     case 0x0f57:        /* dynamite (lit 2) */
5963       element = EL_EM_DYNAMITE_ACTIVE;
5964       break;
5965
5966     case 0x0f5f:        /* dynamite (lit 3) */
5967       element = EL_EM_DYNAMITE_ACTIVE;
5968       break;
5969
5970     case 0x0f67:        /* dynamite (lit 4) */
5971       element = EL_EM_DYNAMITE_ACTIVE;
5972       break;
5973
5974     case 0x0f81:
5975     case 0x0f82:
5976     case 0x0f83:
5977     case 0x0f84:
5978       element = EL_AMOEBA_WET;
5979       break;
5980
5981     case 0x0f85:
5982       element = EL_AMOEBA_DROP;
5983       break;
5984
5985     case 0x0fb9:
5986       element = EL_DC_MAGIC_WALL;
5987       break;
5988
5989     case 0x0fd0:
5990       element = EL_SPACESHIP_UP;
5991       break;
5992
5993     case 0x0fd9:
5994       element = EL_SPACESHIP_DOWN;
5995       break;
5996
5997     case 0x0ff1:
5998       element = EL_SPACESHIP_LEFT;
5999       break;
6000
6001     case 0x0ff9:
6002       element = EL_SPACESHIP_RIGHT;
6003       break;
6004
6005     case 0x1057:
6006       element = EL_BUG_UP;
6007       break;
6008
6009     case 0x1060:
6010       element = EL_BUG_DOWN;
6011       break;
6012
6013     case 0x1078:
6014       element = EL_BUG_LEFT;
6015       break;
6016
6017     case 0x1080:
6018       element = EL_BUG_RIGHT;
6019       break;
6020
6021     case 0x10de:
6022       element = EL_MOLE_UP;
6023       break;
6024
6025     case 0x10e7:
6026       element = EL_MOLE_DOWN;
6027       break;
6028
6029     case 0x10ff:
6030       element = EL_MOLE_LEFT;
6031       break;
6032
6033     case 0x1107:
6034       element = EL_MOLE_RIGHT;
6035       break;
6036
6037     case 0x11c0:
6038       element = EL_ROBOT;
6039       break;
6040
6041     case 0x13f5:
6042       element = EL_YAMYAM;
6043       break;
6044
6045     case 0x1425:
6046       element = EL_SWITCHGATE_OPEN;
6047       break;
6048
6049     case 0x1426:
6050       element = EL_SWITCHGATE_CLOSED;
6051       break;
6052
6053     case 0x1437:
6054       element = EL_DC_SWITCHGATE_SWITCH_UP;
6055       break;
6056
6057     case 0x143a:
6058       element = EL_TIMEGATE_CLOSED;
6059       break;
6060
6061     case 0x144c:        /* conveyor belt switch (green) */
6062       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
6063       break;
6064
6065     case 0x144f:        /* conveyor belt switch (red) */
6066       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
6067       break;
6068
6069     case 0x1452:        /* conveyor belt switch (blue) */
6070       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
6071       break;
6072
6073     case 0x145b:
6074       element = EL_CONVEYOR_BELT_3_MIDDLE;
6075       break;
6076
6077     case 0x1463:
6078       element = EL_CONVEYOR_BELT_3_LEFT;
6079       break;
6080
6081     case 0x146b:
6082       element = EL_CONVEYOR_BELT_3_RIGHT;
6083       break;
6084
6085     case 0x1473:
6086       element = EL_CONVEYOR_BELT_1_MIDDLE;
6087       break;
6088
6089     case 0x147b:
6090       element = EL_CONVEYOR_BELT_1_LEFT;
6091       break;
6092
6093     case 0x1483:
6094       element = EL_CONVEYOR_BELT_1_RIGHT;
6095       break;
6096
6097     case 0x148b:
6098       element = EL_CONVEYOR_BELT_4_MIDDLE;
6099       break;
6100
6101     case 0x1493:
6102       element = EL_CONVEYOR_BELT_4_LEFT;
6103       break;
6104
6105     case 0x149b:
6106       element = EL_CONVEYOR_BELT_4_RIGHT;
6107       break;
6108
6109     case 0x14ac:
6110       element = EL_EXPANDABLE_WALL_HORIZONTAL;
6111       break;
6112
6113     case 0x14bd:
6114       element = EL_EXPANDABLE_WALL_VERTICAL;
6115       break;
6116
6117     case 0x14c6:
6118       element = EL_EXPANDABLE_WALL_ANY;
6119       break;
6120
6121     case 0x14ce:        /* growing steel wall (left/right) */
6122       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
6123       break;
6124
6125     case 0x14df:        /* growing steel wall (up/down) */
6126       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
6127       break;
6128
6129     case 0x14e8:        /* growing steel wall (up/down/left/right) */
6130       element = EL_EXPANDABLE_STEELWALL_ANY;
6131       break;
6132
6133     case 0x14e9:
6134       element = EL_SHIELD_DEADLY;
6135       break;
6136
6137     case 0x1501:
6138       element = EL_EXTRA_TIME;
6139       break;
6140
6141     case 0x154f:
6142       element = EL_ACID;
6143       break;
6144
6145     case 0x1577:
6146       element = EL_EMPTY_SPACE;
6147       break;
6148
6149     case 0x1578:        /* quicksand (empty) */
6150       element = EL_QUICKSAND_FAST_EMPTY;
6151       break;
6152
6153     case 0x1579:        /* slow quicksand (empty) */
6154       element = EL_QUICKSAND_EMPTY;
6155       break;
6156
6157       /* 0x157c - 0x158b: */
6158       /* EL_SAND */
6159
6160       /* 0x1590 - 0x159f: */
6161       /* EL_DC_LANDMINE */
6162
6163     case 0x15a0:
6164       element = EL_EM_DYNAMITE;
6165       break;
6166
6167     case 0x15a1:        /* key (red) */
6168       element = EL_EM_KEY_1;
6169       break;
6170
6171     case 0x15a2:        /* key (yellow) */
6172       element = EL_EM_KEY_2;
6173       break;
6174
6175     case 0x15a3:        /* key (blue) */
6176       element = EL_EM_KEY_4;
6177       break;
6178
6179     case 0x15a4:        /* key (green) */
6180       element = EL_EM_KEY_3;
6181       break;
6182
6183     case 0x15a5:        /* key (white) */
6184       element = EL_DC_KEY_WHITE;
6185       break;
6186
6187     case 0x15a6:
6188       element = EL_WALL_SLIPPERY;
6189       break;
6190
6191     case 0x15a7:
6192       element = EL_WALL;
6193       break;
6194
6195     case 0x15a8:        /* wall (not round) */
6196       element = EL_WALL;
6197       break;
6198
6199     case 0x15a9:        /* (blue) */
6200       element = EL_CHAR_A;
6201       break;
6202
6203     case 0x15aa:        /* (blue) */
6204       element = EL_CHAR_B;
6205       break;
6206
6207     case 0x15ab:        /* (blue) */
6208       element = EL_CHAR_C;
6209       break;
6210
6211     case 0x15ac:        /* (blue) */
6212       element = EL_CHAR_D;
6213       break;
6214
6215     case 0x15ad:        /* (blue) */
6216       element = EL_CHAR_E;
6217       break;
6218
6219     case 0x15ae:        /* (blue) */
6220       element = EL_CHAR_F;
6221       break;
6222
6223     case 0x15af:        /* (blue) */
6224       element = EL_CHAR_G;
6225       break;
6226
6227     case 0x15b0:        /* (blue) */
6228       element = EL_CHAR_H;
6229       break;
6230
6231     case 0x15b1:        /* (blue) */
6232       element = EL_CHAR_I;
6233       break;
6234
6235     case 0x15b2:        /* (blue) */
6236       element = EL_CHAR_J;
6237       break;
6238
6239     case 0x15b3:        /* (blue) */
6240       element = EL_CHAR_K;
6241       break;
6242
6243     case 0x15b4:        /* (blue) */
6244       element = EL_CHAR_L;
6245       break;
6246
6247     case 0x15b5:        /* (blue) */
6248       element = EL_CHAR_M;
6249       break;
6250
6251     case 0x15b6:        /* (blue) */
6252       element = EL_CHAR_N;
6253       break;
6254
6255     case 0x15b7:        /* (blue) */
6256       element = EL_CHAR_O;
6257       break;
6258
6259     case 0x15b8:        /* (blue) */
6260       element = EL_CHAR_P;
6261       break;
6262
6263     case 0x15b9:        /* (blue) */
6264       element = EL_CHAR_Q;
6265       break;
6266
6267     case 0x15ba:        /* (blue) */
6268       element = EL_CHAR_R;
6269       break;
6270
6271     case 0x15bb:        /* (blue) */
6272       element = EL_CHAR_S;
6273       break;
6274
6275     case 0x15bc:        /* (blue) */
6276       element = EL_CHAR_T;
6277       break;
6278
6279     case 0x15bd:        /* (blue) */
6280       element = EL_CHAR_U;
6281       break;
6282
6283     case 0x15be:        /* (blue) */
6284       element = EL_CHAR_V;
6285       break;
6286
6287     case 0x15bf:        /* (blue) */
6288       element = EL_CHAR_W;
6289       break;
6290
6291     case 0x15c0:        /* (blue) */
6292       element = EL_CHAR_X;
6293       break;
6294
6295     case 0x15c1:        /* (blue) */
6296       element = EL_CHAR_Y;
6297       break;
6298
6299     case 0x15c2:        /* (blue) */
6300       element = EL_CHAR_Z;
6301       break;
6302
6303     case 0x15c3:        /* (blue) */
6304       element = EL_CHAR_AUMLAUT;
6305       break;
6306
6307     case 0x15c4:        /* (blue) */
6308       element = EL_CHAR_OUMLAUT;
6309       break;
6310
6311     case 0x15c5:        /* (blue) */
6312       element = EL_CHAR_UUMLAUT;
6313       break;
6314
6315     case 0x15c6:        /* (blue) */
6316       element = EL_CHAR_0;
6317       break;
6318
6319     case 0x15c7:        /* (blue) */
6320       element = EL_CHAR_1;
6321       break;
6322
6323     case 0x15c8:        /* (blue) */
6324       element = EL_CHAR_2;
6325       break;
6326
6327     case 0x15c9:        /* (blue) */
6328       element = EL_CHAR_3;
6329       break;
6330
6331     case 0x15ca:        /* (blue) */
6332       element = EL_CHAR_4;
6333       break;
6334
6335     case 0x15cb:        /* (blue) */
6336       element = EL_CHAR_5;
6337       break;
6338
6339     case 0x15cc:        /* (blue) */
6340       element = EL_CHAR_6;
6341       break;
6342
6343     case 0x15cd:        /* (blue) */
6344       element = EL_CHAR_7;
6345       break;
6346
6347     case 0x15ce:        /* (blue) */
6348       element = EL_CHAR_8;
6349       break;
6350
6351     case 0x15cf:        /* (blue) */
6352       element = EL_CHAR_9;
6353       break;
6354
6355     case 0x15d0:        /* (blue) */
6356       element = EL_CHAR_PERIOD;
6357       break;
6358
6359     case 0x15d1:        /* (blue) */
6360       element = EL_CHAR_EXCLAM;
6361       break;
6362
6363     case 0x15d2:        /* (blue) */
6364       element = EL_CHAR_COLON;
6365       break;
6366
6367     case 0x15d3:        /* (blue) */
6368       element = EL_CHAR_LESS;
6369       break;
6370
6371     case 0x15d4:        /* (blue) */
6372       element = EL_CHAR_GREATER;
6373       break;
6374
6375     case 0x15d5:        /* (blue) */
6376       element = EL_CHAR_QUESTION;
6377       break;
6378
6379     case 0x15d6:        /* (blue) */
6380       element = EL_CHAR_COPYRIGHT;
6381       break;
6382
6383     case 0x15d7:        /* (blue) */
6384       element = EL_CHAR_UP;
6385       break;
6386
6387     case 0x15d8:        /* (blue) */
6388       element = EL_CHAR_DOWN;
6389       break;
6390
6391     case 0x15d9:        /* (blue) */
6392       element = EL_CHAR_BUTTON;
6393       break;
6394
6395     case 0x15da:        /* (blue) */
6396       element = EL_CHAR_PLUS;
6397       break;
6398
6399     case 0x15db:        /* (blue) */
6400       element = EL_CHAR_MINUS;
6401       break;
6402
6403     case 0x15dc:        /* (blue) */
6404       element = EL_CHAR_APOSTROPHE;
6405       break;
6406
6407     case 0x15dd:        /* (blue) */
6408       element = EL_CHAR_PARENLEFT;
6409       break;
6410
6411     case 0x15de:        /* (blue) */
6412       element = EL_CHAR_PARENRIGHT;
6413       break;
6414
6415     case 0x15df:        /* (green) */
6416       element = EL_CHAR_A;
6417       break;
6418
6419     case 0x15e0:        /* (green) */
6420       element = EL_CHAR_B;
6421       break;
6422
6423     case 0x15e1:        /* (green) */
6424       element = EL_CHAR_C;
6425       break;
6426
6427     case 0x15e2:        /* (green) */
6428       element = EL_CHAR_D;
6429       break;
6430
6431     case 0x15e3:        /* (green) */
6432       element = EL_CHAR_E;
6433       break;
6434
6435     case 0x15e4:        /* (green) */
6436       element = EL_CHAR_F;
6437       break;
6438
6439     case 0x15e5:        /* (green) */
6440       element = EL_CHAR_G;
6441       break;
6442
6443     case 0x15e6:        /* (green) */
6444       element = EL_CHAR_H;
6445       break;
6446
6447     case 0x15e7:        /* (green) */
6448       element = EL_CHAR_I;
6449       break;
6450
6451     case 0x15e8:        /* (green) */
6452       element = EL_CHAR_J;
6453       break;
6454
6455     case 0x15e9:        /* (green) */
6456       element = EL_CHAR_K;
6457       break;
6458
6459     case 0x15ea:        /* (green) */
6460       element = EL_CHAR_L;
6461       break;
6462
6463     case 0x15eb:        /* (green) */
6464       element = EL_CHAR_M;
6465       break;
6466
6467     case 0x15ec:        /* (green) */
6468       element = EL_CHAR_N;
6469       break;
6470
6471     case 0x15ed:        /* (green) */
6472       element = EL_CHAR_O;
6473       break;
6474
6475     case 0x15ee:        /* (green) */
6476       element = EL_CHAR_P;
6477       break;
6478
6479     case 0x15ef:        /* (green) */
6480       element = EL_CHAR_Q;
6481       break;
6482
6483     case 0x15f0:        /* (green) */
6484       element = EL_CHAR_R;
6485       break;
6486
6487     case 0x15f1:        /* (green) */
6488       element = EL_CHAR_S;
6489       break;
6490
6491     case 0x15f2:        /* (green) */
6492       element = EL_CHAR_T;
6493       break;
6494
6495     case 0x15f3:        /* (green) */
6496       element = EL_CHAR_U;
6497       break;
6498
6499     case 0x15f4:        /* (green) */
6500       element = EL_CHAR_V;
6501       break;
6502
6503     case 0x15f5:        /* (green) */
6504       element = EL_CHAR_W;
6505       break;
6506
6507     case 0x15f6:        /* (green) */
6508       element = EL_CHAR_X;
6509       break;
6510
6511     case 0x15f7:        /* (green) */
6512       element = EL_CHAR_Y;
6513       break;
6514
6515     case 0x15f8:        /* (green) */
6516       element = EL_CHAR_Z;
6517       break;
6518
6519     case 0x15f9:        /* (green) */
6520       element = EL_CHAR_AUMLAUT;
6521       break;
6522
6523     case 0x15fa:        /* (green) */
6524       element = EL_CHAR_OUMLAUT;
6525       break;
6526
6527     case 0x15fb:        /* (green) */
6528       element = EL_CHAR_UUMLAUT;
6529       break;
6530
6531     case 0x15fc:        /* (green) */
6532       element = EL_CHAR_0;
6533       break;
6534
6535     case 0x15fd:        /* (green) */
6536       element = EL_CHAR_1;
6537       break;
6538
6539     case 0x15fe:        /* (green) */
6540       element = EL_CHAR_2;
6541       break;
6542
6543     case 0x15ff:        /* (green) */
6544       element = EL_CHAR_3;
6545       break;
6546
6547     case 0x1600:        /* (green) */
6548       element = EL_CHAR_4;
6549       break;
6550
6551     case 0x1601:        /* (green) */
6552       element = EL_CHAR_5;
6553       break;
6554
6555     case 0x1602:        /* (green) */
6556       element = EL_CHAR_6;
6557       break;
6558
6559     case 0x1603:        /* (green) */
6560       element = EL_CHAR_7;
6561       break;
6562
6563     case 0x1604:        /* (green) */
6564       element = EL_CHAR_8;
6565       break;
6566
6567     case 0x1605:        /* (green) */
6568       element = EL_CHAR_9;
6569       break;
6570
6571     case 0x1606:        /* (green) */
6572       element = EL_CHAR_PERIOD;
6573       break;
6574
6575     case 0x1607:        /* (green) */
6576       element = EL_CHAR_EXCLAM;
6577       break;
6578
6579     case 0x1608:        /* (green) */
6580       element = EL_CHAR_COLON;
6581       break;
6582
6583     case 0x1609:        /* (green) */
6584       element = EL_CHAR_LESS;
6585       break;
6586
6587     case 0x160a:        /* (green) */
6588       element = EL_CHAR_GREATER;
6589       break;
6590
6591     case 0x160b:        /* (green) */
6592       element = EL_CHAR_QUESTION;
6593       break;
6594
6595     case 0x160c:        /* (green) */
6596       element = EL_CHAR_COPYRIGHT;
6597       break;
6598
6599     case 0x160d:        /* (green) */
6600       element = EL_CHAR_UP;
6601       break;
6602
6603     case 0x160e:        /* (green) */
6604       element = EL_CHAR_DOWN;
6605       break;
6606
6607     case 0x160f:        /* (green) */
6608       element = EL_CHAR_BUTTON;
6609       break;
6610
6611     case 0x1610:        /* (green) */
6612       element = EL_CHAR_PLUS;
6613       break;
6614
6615     case 0x1611:        /* (green) */
6616       element = EL_CHAR_MINUS;
6617       break;
6618
6619     case 0x1612:        /* (green) */
6620       element = EL_CHAR_APOSTROPHE;
6621       break;
6622
6623     case 0x1613:        /* (green) */
6624       element = EL_CHAR_PARENLEFT;
6625       break;
6626
6627     case 0x1614:        /* (green) */
6628       element = EL_CHAR_PARENRIGHT;
6629       break;
6630
6631     case 0x1615:        /* (blue steel) */
6632       element = EL_STEEL_CHAR_A;
6633       break;
6634
6635     case 0x1616:        /* (blue steel) */
6636       element = EL_STEEL_CHAR_B;
6637       break;
6638
6639     case 0x1617:        /* (blue steel) */
6640       element = EL_STEEL_CHAR_C;
6641       break;
6642
6643     case 0x1618:        /* (blue steel) */
6644       element = EL_STEEL_CHAR_D;
6645       break;
6646
6647     case 0x1619:        /* (blue steel) */
6648       element = EL_STEEL_CHAR_E;
6649       break;
6650
6651     case 0x161a:        /* (blue steel) */
6652       element = EL_STEEL_CHAR_F;
6653       break;
6654
6655     case 0x161b:        /* (blue steel) */
6656       element = EL_STEEL_CHAR_G;
6657       break;
6658
6659     case 0x161c:        /* (blue steel) */
6660       element = EL_STEEL_CHAR_H;
6661       break;
6662
6663     case 0x161d:        /* (blue steel) */
6664       element = EL_STEEL_CHAR_I;
6665       break;
6666
6667     case 0x161e:        /* (blue steel) */
6668       element = EL_STEEL_CHAR_J;
6669       break;
6670
6671     case 0x161f:        /* (blue steel) */
6672       element = EL_STEEL_CHAR_K;
6673       break;
6674
6675     case 0x1620:        /* (blue steel) */
6676       element = EL_STEEL_CHAR_L;
6677       break;
6678
6679     case 0x1621:        /* (blue steel) */
6680       element = EL_STEEL_CHAR_M;
6681       break;
6682
6683     case 0x1622:        /* (blue steel) */
6684       element = EL_STEEL_CHAR_N;
6685       break;
6686
6687     case 0x1623:        /* (blue steel) */
6688       element = EL_STEEL_CHAR_O;
6689       break;
6690
6691     case 0x1624:        /* (blue steel) */
6692       element = EL_STEEL_CHAR_P;
6693       break;
6694
6695     case 0x1625:        /* (blue steel) */
6696       element = EL_STEEL_CHAR_Q;
6697       break;
6698
6699     case 0x1626:        /* (blue steel) */
6700       element = EL_STEEL_CHAR_R;
6701       break;
6702
6703     case 0x1627:        /* (blue steel) */
6704       element = EL_STEEL_CHAR_S;
6705       break;
6706
6707     case 0x1628:        /* (blue steel) */
6708       element = EL_STEEL_CHAR_T;
6709       break;
6710
6711     case 0x1629:        /* (blue steel) */
6712       element = EL_STEEL_CHAR_U;
6713       break;
6714
6715     case 0x162a:        /* (blue steel) */
6716       element = EL_STEEL_CHAR_V;
6717       break;
6718
6719     case 0x162b:        /* (blue steel) */
6720       element = EL_STEEL_CHAR_W;
6721       break;
6722
6723     case 0x162c:        /* (blue steel) */
6724       element = EL_STEEL_CHAR_X;
6725       break;
6726
6727     case 0x162d:        /* (blue steel) */
6728       element = EL_STEEL_CHAR_Y;
6729       break;
6730
6731     case 0x162e:        /* (blue steel) */
6732       element = EL_STEEL_CHAR_Z;
6733       break;
6734
6735     case 0x162f:        /* (blue steel) */
6736       element = EL_STEEL_CHAR_AUMLAUT;
6737       break;
6738
6739     case 0x1630:        /* (blue steel) */
6740       element = EL_STEEL_CHAR_OUMLAUT;
6741       break;
6742
6743     case 0x1631:        /* (blue steel) */
6744       element = EL_STEEL_CHAR_UUMLAUT;
6745       break;
6746
6747     case 0x1632:        /* (blue steel) */
6748       element = EL_STEEL_CHAR_0;
6749       break;
6750
6751     case 0x1633:        /* (blue steel) */
6752       element = EL_STEEL_CHAR_1;
6753       break;
6754
6755     case 0x1634:        /* (blue steel) */
6756       element = EL_STEEL_CHAR_2;
6757       break;
6758
6759     case 0x1635:        /* (blue steel) */
6760       element = EL_STEEL_CHAR_3;
6761       break;
6762
6763     case 0x1636:        /* (blue steel) */
6764       element = EL_STEEL_CHAR_4;
6765       break;
6766
6767     case 0x1637:        /* (blue steel) */
6768       element = EL_STEEL_CHAR_5;
6769       break;
6770
6771     case 0x1638:        /* (blue steel) */
6772       element = EL_STEEL_CHAR_6;
6773       break;
6774
6775     case 0x1639:        /* (blue steel) */
6776       element = EL_STEEL_CHAR_7;
6777       break;
6778
6779     case 0x163a:        /* (blue steel) */
6780       element = EL_STEEL_CHAR_8;
6781       break;
6782
6783     case 0x163b:        /* (blue steel) */
6784       element = EL_STEEL_CHAR_9;
6785       break;
6786
6787     case 0x163c:        /* (blue steel) */
6788       element = EL_STEEL_CHAR_PERIOD;
6789       break;
6790
6791     case 0x163d:        /* (blue steel) */
6792       element = EL_STEEL_CHAR_EXCLAM;
6793       break;
6794
6795     case 0x163e:        /* (blue steel) */
6796       element = EL_STEEL_CHAR_COLON;
6797       break;
6798
6799     case 0x163f:        /* (blue steel) */
6800       element = EL_STEEL_CHAR_LESS;
6801       break;
6802
6803     case 0x1640:        /* (blue steel) */
6804       element = EL_STEEL_CHAR_GREATER;
6805       break;
6806
6807     case 0x1641:        /* (blue steel) */
6808       element = EL_STEEL_CHAR_QUESTION;
6809       break;
6810
6811     case 0x1642:        /* (blue steel) */
6812       element = EL_STEEL_CHAR_COPYRIGHT;
6813       break;
6814
6815     case 0x1643:        /* (blue steel) */
6816       element = EL_STEEL_CHAR_UP;
6817       break;
6818
6819     case 0x1644:        /* (blue steel) */
6820       element = EL_STEEL_CHAR_DOWN;
6821       break;
6822
6823     case 0x1645:        /* (blue steel) */
6824       element = EL_STEEL_CHAR_BUTTON;
6825       break;
6826
6827     case 0x1646:        /* (blue steel) */
6828       element = EL_STEEL_CHAR_PLUS;
6829       break;
6830
6831     case 0x1647:        /* (blue steel) */
6832       element = EL_STEEL_CHAR_MINUS;
6833       break;
6834
6835     case 0x1648:        /* (blue steel) */
6836       element = EL_STEEL_CHAR_APOSTROPHE;
6837       break;
6838
6839     case 0x1649:        /* (blue steel) */
6840       element = EL_STEEL_CHAR_PARENLEFT;
6841       break;
6842
6843     case 0x164a:        /* (blue steel) */
6844       element = EL_STEEL_CHAR_PARENRIGHT;
6845       break;
6846
6847     case 0x164b:        /* (green steel) */
6848       element = EL_STEEL_CHAR_A;
6849       break;
6850
6851     case 0x164c:        /* (green steel) */
6852       element = EL_STEEL_CHAR_B;
6853       break;
6854
6855     case 0x164d:        /* (green steel) */
6856       element = EL_STEEL_CHAR_C;
6857       break;
6858
6859     case 0x164e:        /* (green steel) */
6860       element = EL_STEEL_CHAR_D;
6861       break;
6862
6863     case 0x164f:        /* (green steel) */
6864       element = EL_STEEL_CHAR_E;
6865       break;
6866
6867     case 0x1650:        /* (green steel) */
6868       element = EL_STEEL_CHAR_F;
6869       break;
6870
6871     case 0x1651:        /* (green steel) */
6872       element = EL_STEEL_CHAR_G;
6873       break;
6874
6875     case 0x1652:        /* (green steel) */
6876       element = EL_STEEL_CHAR_H;
6877       break;
6878
6879     case 0x1653:        /* (green steel) */
6880       element = EL_STEEL_CHAR_I;
6881       break;
6882
6883     case 0x1654:        /* (green steel) */
6884       element = EL_STEEL_CHAR_J;
6885       break;
6886
6887     case 0x1655:        /* (green steel) */
6888       element = EL_STEEL_CHAR_K;
6889       break;
6890
6891     case 0x1656:        /* (green steel) */
6892       element = EL_STEEL_CHAR_L;
6893       break;
6894
6895     case 0x1657:        /* (green steel) */
6896       element = EL_STEEL_CHAR_M;
6897       break;
6898
6899     case 0x1658:        /* (green steel) */
6900       element = EL_STEEL_CHAR_N;
6901       break;
6902
6903     case 0x1659:        /* (green steel) */
6904       element = EL_STEEL_CHAR_O;
6905       break;
6906
6907     case 0x165a:        /* (green steel) */
6908       element = EL_STEEL_CHAR_P;
6909       break;
6910
6911     case 0x165b:        /* (green steel) */
6912       element = EL_STEEL_CHAR_Q;
6913       break;
6914
6915     case 0x165c:        /* (green steel) */
6916       element = EL_STEEL_CHAR_R;
6917       break;
6918
6919     case 0x165d:        /* (green steel) */
6920       element = EL_STEEL_CHAR_S;
6921       break;
6922
6923     case 0x165e:        /* (green steel) */
6924       element = EL_STEEL_CHAR_T;
6925       break;
6926
6927     case 0x165f:        /* (green steel) */
6928       element = EL_STEEL_CHAR_U;
6929       break;
6930
6931     case 0x1660:        /* (green steel) */
6932       element = EL_STEEL_CHAR_V;
6933       break;
6934
6935     case 0x1661:        /* (green steel) */
6936       element = EL_STEEL_CHAR_W;
6937       break;
6938
6939     case 0x1662:        /* (green steel) */
6940       element = EL_STEEL_CHAR_X;
6941       break;
6942
6943     case 0x1663:        /* (green steel) */
6944       element = EL_STEEL_CHAR_Y;
6945       break;
6946
6947     case 0x1664:        /* (green steel) */
6948       element = EL_STEEL_CHAR_Z;
6949       break;
6950
6951     case 0x1665:        /* (green steel) */
6952       element = EL_STEEL_CHAR_AUMLAUT;
6953       break;
6954
6955     case 0x1666:        /* (green steel) */
6956       element = EL_STEEL_CHAR_OUMLAUT;
6957       break;
6958
6959     case 0x1667:        /* (green steel) */
6960       element = EL_STEEL_CHAR_UUMLAUT;
6961       break;
6962
6963     case 0x1668:        /* (green steel) */
6964       element = EL_STEEL_CHAR_0;
6965       break;
6966
6967     case 0x1669:        /* (green steel) */
6968       element = EL_STEEL_CHAR_1;
6969       break;
6970
6971     case 0x166a:        /* (green steel) */
6972       element = EL_STEEL_CHAR_2;
6973       break;
6974
6975     case 0x166b:        /* (green steel) */
6976       element = EL_STEEL_CHAR_3;
6977       break;
6978
6979     case 0x166c:        /* (green steel) */
6980       element = EL_STEEL_CHAR_4;
6981       break;
6982
6983     case 0x166d:        /* (green steel) */
6984       element = EL_STEEL_CHAR_5;
6985       break;
6986
6987     case 0x166e:        /* (green steel) */
6988       element = EL_STEEL_CHAR_6;
6989       break;
6990
6991     case 0x166f:        /* (green steel) */
6992       element = EL_STEEL_CHAR_7;
6993       break;
6994
6995     case 0x1670:        /* (green steel) */
6996       element = EL_STEEL_CHAR_8;
6997       break;
6998
6999     case 0x1671:        /* (green steel) */
7000       element = EL_STEEL_CHAR_9;
7001       break;
7002
7003     case 0x1672:        /* (green steel) */
7004       element = EL_STEEL_CHAR_PERIOD;
7005       break;
7006
7007     case 0x1673:        /* (green steel) */
7008       element = EL_STEEL_CHAR_EXCLAM;
7009       break;
7010
7011     case 0x1674:        /* (green steel) */
7012       element = EL_STEEL_CHAR_COLON;
7013       break;
7014
7015     case 0x1675:        /* (green steel) */
7016       element = EL_STEEL_CHAR_LESS;
7017       break;
7018
7019     case 0x1676:        /* (green steel) */
7020       element = EL_STEEL_CHAR_GREATER;
7021       break;
7022
7023     case 0x1677:        /* (green steel) */
7024       element = EL_STEEL_CHAR_QUESTION;
7025       break;
7026
7027     case 0x1678:        /* (green steel) */
7028       element = EL_STEEL_CHAR_COPYRIGHT;
7029       break;
7030
7031     case 0x1679:        /* (green steel) */
7032       element = EL_STEEL_CHAR_UP;
7033       break;
7034
7035     case 0x167a:        /* (green steel) */
7036       element = EL_STEEL_CHAR_DOWN;
7037       break;
7038
7039     case 0x167b:        /* (green steel) */
7040       element = EL_STEEL_CHAR_BUTTON;
7041       break;
7042
7043     case 0x167c:        /* (green steel) */
7044       element = EL_STEEL_CHAR_PLUS;
7045       break;
7046
7047     case 0x167d:        /* (green steel) */
7048       element = EL_STEEL_CHAR_MINUS;
7049       break;
7050
7051     case 0x167e:        /* (green steel) */
7052       element = EL_STEEL_CHAR_APOSTROPHE;
7053       break;
7054
7055     case 0x167f:        /* (green steel) */
7056       element = EL_STEEL_CHAR_PARENLEFT;
7057       break;
7058
7059     case 0x1680:        /* (green steel) */
7060       element = EL_STEEL_CHAR_PARENRIGHT;
7061       break;
7062
7063     case 0x1681:        /* gate (red) */
7064       element = EL_EM_GATE_1;
7065       break;
7066
7067     case 0x1682:        /* secret gate (red) */
7068       element = EL_GATE_1_GRAY;
7069       break;
7070
7071     case 0x1683:        /* gate (yellow) */
7072       element = EL_EM_GATE_2;
7073       break;
7074
7075     case 0x1684:        /* secret gate (yellow) */
7076       element = EL_GATE_2_GRAY;
7077       break;
7078
7079     case 0x1685:        /* gate (blue) */
7080       element = EL_EM_GATE_4;
7081       break;
7082
7083     case 0x1686:        /* secret gate (blue) */
7084       element = EL_GATE_4_GRAY;
7085       break;
7086
7087     case 0x1687:        /* gate (green) */
7088       element = EL_EM_GATE_3;
7089       break;
7090
7091     case 0x1688:        /* secret gate (green) */
7092       element = EL_GATE_3_GRAY;
7093       break;
7094
7095     case 0x1689:        /* gate (white) */
7096       element = EL_DC_GATE_WHITE;
7097       break;
7098
7099     case 0x168a:        /* secret gate (white) */
7100       element = EL_DC_GATE_WHITE_GRAY;
7101       break;
7102
7103     case 0x168b:        /* secret gate (no key) */
7104       element = EL_DC_GATE_FAKE_GRAY;
7105       break;
7106
7107     case 0x168c:
7108       element = EL_ROBOT_WHEEL;
7109       break;
7110
7111     case 0x168d:
7112       element = EL_DC_TIMEGATE_SWITCH;
7113       break;
7114
7115     case 0x168e:
7116       element = EL_ACID_POOL_BOTTOM;
7117       break;
7118
7119     case 0x168f:
7120       element = EL_ACID_POOL_TOPLEFT;
7121       break;
7122
7123     case 0x1690:
7124       element = EL_ACID_POOL_TOPRIGHT;
7125       break;
7126
7127     case 0x1691:
7128       element = EL_ACID_POOL_BOTTOMLEFT;
7129       break;
7130
7131     case 0x1692:
7132       element = EL_ACID_POOL_BOTTOMRIGHT;
7133       break;
7134
7135     case 0x1693:
7136       element = EL_STEELWALL;
7137       break;
7138
7139     case 0x1694:
7140       element = EL_STEELWALL_SLIPPERY;
7141       break;
7142
7143     case 0x1695:        /* steel wall (not round) */
7144       element = EL_STEELWALL;
7145       break;
7146
7147     case 0x1696:        /* steel wall (left) */
7148       element = EL_DC_STEELWALL_1_LEFT;
7149       break;
7150
7151     case 0x1697:        /* steel wall (bottom) */
7152       element = EL_DC_STEELWALL_1_BOTTOM;
7153       break;
7154
7155     case 0x1698:        /* steel wall (right) */
7156       element = EL_DC_STEELWALL_1_RIGHT;
7157       break;
7158
7159     case 0x1699:        /* steel wall (top) */
7160       element = EL_DC_STEELWALL_1_TOP;
7161       break;
7162
7163     case 0x169a:        /* steel wall (left/bottom) */
7164       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
7165       break;
7166
7167     case 0x169b:        /* steel wall (right/bottom) */
7168       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
7169       break;
7170
7171     case 0x169c:        /* steel wall (right/top) */
7172       element = EL_DC_STEELWALL_1_TOPRIGHT;
7173       break;
7174
7175     case 0x169d:        /* steel wall (left/top) */
7176       element = EL_DC_STEELWALL_1_TOPLEFT;
7177       break;
7178
7179     case 0x169e:        /* steel wall (right/bottom small) */
7180       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
7181       break;
7182
7183     case 0x169f:        /* steel wall (left/bottom small) */
7184       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
7185       break;
7186
7187     case 0x16a0:        /* steel wall (right/top small) */
7188       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
7189       break;
7190
7191     case 0x16a1:        /* steel wall (left/top small) */
7192       element = EL_DC_STEELWALL_1_TOPLEFT_2;
7193       break;
7194
7195     case 0x16a2:        /* steel wall (left/right) */
7196       element = EL_DC_STEELWALL_1_VERTICAL;
7197       break;
7198
7199     case 0x16a3:        /* steel wall (top/bottom) */
7200       element = EL_DC_STEELWALL_1_HORIZONTAL;
7201       break;
7202
7203     case 0x16a4:        /* steel wall 2 (left end) */
7204       element = EL_DC_STEELWALL_2_LEFT;
7205       break;
7206
7207     case 0x16a5:        /* steel wall 2 (right end) */
7208       element = EL_DC_STEELWALL_2_RIGHT;
7209       break;
7210
7211     case 0x16a6:        /* steel wall 2 (top end) */
7212       element = EL_DC_STEELWALL_2_TOP;
7213       break;
7214
7215     case 0x16a7:        /* steel wall 2 (bottom end) */
7216       element = EL_DC_STEELWALL_2_BOTTOM;
7217       break;
7218
7219     case 0x16a8:        /* steel wall 2 (left/right) */
7220       element = EL_DC_STEELWALL_2_HORIZONTAL;
7221       break;
7222
7223     case 0x16a9:        /* steel wall 2 (up/down) */
7224       element = EL_DC_STEELWALL_2_VERTICAL;
7225       break;
7226
7227     case 0x16aa:        /* steel wall 2 (mid) */
7228       element = EL_DC_STEELWALL_2_MIDDLE;
7229       break;
7230
7231     case 0x16ab:
7232       element = EL_SIGN_EXCLAMATION;
7233       break;
7234
7235     case 0x16ac:
7236       element = EL_SIGN_RADIOACTIVITY;
7237       break;
7238
7239     case 0x16ad:
7240       element = EL_SIGN_STOP;
7241       break;
7242
7243     case 0x16ae:
7244       element = EL_SIGN_WHEELCHAIR;
7245       break;
7246
7247     case 0x16af:
7248       element = EL_SIGN_PARKING;
7249       break;
7250
7251     case 0x16b0:
7252       element = EL_SIGN_NO_ENTRY;
7253       break;
7254
7255     case 0x16b1:
7256       element = EL_SIGN_HEART;
7257       break;
7258
7259     case 0x16b2:
7260       element = EL_SIGN_GIVE_WAY;
7261       break;
7262
7263     case 0x16b3:
7264       element = EL_SIGN_ENTRY_FORBIDDEN;
7265       break;
7266
7267     case 0x16b4:
7268       element = EL_SIGN_EMERGENCY_EXIT;
7269       break;
7270
7271     case 0x16b5:
7272       element = EL_SIGN_YIN_YANG;
7273       break;
7274
7275     case 0x16b6:
7276       element = EL_WALL_EMERALD;
7277       break;
7278
7279     case 0x16b7:
7280       element = EL_WALL_DIAMOND;
7281       break;
7282
7283     case 0x16b8:
7284       element = EL_WALL_PEARL;
7285       break;
7286
7287     case 0x16b9:
7288       element = EL_WALL_CRYSTAL;
7289       break;
7290
7291     case 0x16ba:
7292       element = EL_INVISIBLE_WALL;
7293       break;
7294
7295     case 0x16bb:
7296       element = EL_INVISIBLE_STEELWALL;
7297       break;
7298
7299       /* 0x16bc - 0x16cb: */
7300       /* EL_INVISIBLE_SAND */
7301
7302     case 0x16cc:
7303       element = EL_LIGHT_SWITCH;
7304       break;
7305
7306     case 0x16cd:
7307       element = EL_ENVELOPE_1;
7308       break;
7309
7310     default:
7311       if (element >= 0x0117 && element <= 0x036e)       /* (?) */
7312         element = EL_DIAMOND;
7313       else if (element >= 0x042d && element <= 0x0684)  /* (?) */
7314         element = EL_EMERALD;
7315       else if (element >= 0x157c && element <= 0x158b)
7316         element = EL_SAND;
7317       else if (element >= 0x1590 && element <= 0x159f)
7318         element = EL_DC_LANDMINE;
7319       else if (element >= 0x16bc && element <= 0x16cb)
7320         element = EL_INVISIBLE_SAND;
7321       else
7322       {
7323         Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
7324         element = EL_UNKNOWN;
7325       }
7326       break;
7327   }
7328
7329   return getMappedElement(element);
7330 }
7331
7332 #if 1
7333
7334 #if 1
7335
7336 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
7337                                        int nr)
7338 {
7339   byte header[DC_LEVEL_HEADER_SIZE];
7340   int envelope_size;
7341   int envelope_header_pos = 62;
7342   int envelope_content_pos = 94;
7343   int level_name_pos = 251;
7344   int level_author_pos = 292;
7345   int envelope_header_len;
7346   int envelope_content_len;
7347   int level_name_len;
7348   int level_author_len;
7349   int fieldx, fieldy;
7350   int num_yamyam_contents;
7351   int i, x, y;
7352
7353   getDecodedWord_DC(0, TRUE);           /* initialize DC2 decoding engine */
7354
7355   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
7356   {
7357     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7358
7359     header[i * 2 + 0] = header_word >> 8;
7360     header[i * 2 + 1] = header_word & 0xff;
7361   }
7362
7363   /* read some values from level header to check level decoding integrity */
7364   fieldx = header[6] | (header[7] << 8);
7365   fieldy = header[8] | (header[9] << 8);
7366   num_yamyam_contents = header[60] | (header[61] << 8);
7367
7368   /* do some simple sanity checks to ensure that level was correctly decoded */
7369   if (fieldx < 1 || fieldx > 256 ||
7370       fieldy < 1 || fieldy > 256 ||
7371       num_yamyam_contents < 1 || num_yamyam_contents > 8)
7372   {
7373     level->no_valid_file = TRUE;
7374
7375     Error(ERR_WARN, "cannot decode level from stream -- using empty level");
7376
7377     return;
7378   }
7379
7380   /* maximum envelope header size is 31 bytes */
7381   envelope_header_len   = header[envelope_header_pos];
7382   /* maximum envelope content size is 110 (156?) bytes */
7383   envelope_content_len  = header[envelope_content_pos];
7384
7385   /* maximum level title size is 40 bytes */
7386   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
7387   /* maximum level author size is 30 (51?) bytes */
7388   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
7389
7390   envelope_size = 0;
7391
7392   for (i = 0; i < envelope_header_len; i++)
7393     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7394       level->envelope[0].text[envelope_size++] =
7395         header[envelope_header_pos + 1 + i];
7396
7397   if (envelope_header_len > 0 && envelope_content_len > 0)
7398   {
7399     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7400       level->envelope[0].text[envelope_size++] = '\n';
7401     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7402       level->envelope[0].text[envelope_size++] = '\n';
7403   }
7404
7405   for (i = 0; i < envelope_content_len; i++)
7406     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7407       level->envelope[0].text[envelope_size++] =
7408         header[envelope_content_pos + 1 + i];
7409
7410   level->envelope[0].text[envelope_size] = '\0';
7411
7412   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
7413   level->envelope[0].ysize = 10;
7414   level->envelope[0].autowrap = TRUE;
7415   level->envelope[0].centered = TRUE;
7416
7417   for (i = 0; i < level_name_len; i++)
7418     level->name[i] = header[level_name_pos + 1 + i];
7419   level->name[level_name_len] = '\0';
7420
7421   for (i = 0; i < level_author_len; i++)
7422     level->author[i] = header[level_author_pos + 1 + i];
7423   level->author[level_author_len] = '\0';
7424
7425   num_yamyam_contents = header[60] | (header[61] << 8);
7426   level->num_yamyam_contents =
7427     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
7428
7429   for (i = 0; i < num_yamyam_contents; i++)
7430   {
7431     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
7432     {
7433       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7434 #if 1
7435       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7436 #else
7437       int element_dc = word;
7438 #endif
7439
7440       if (i < MAX_ELEMENT_CONTENTS)
7441         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
7442     }
7443   }
7444
7445   fieldx = header[6] | (header[7] << 8);
7446   fieldy = header[8] | (header[9] << 8);
7447   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
7448   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
7449
7450   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
7451   {
7452     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7453 #if 1
7454     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7455 #else
7456     int element_dc = word;
7457 #endif
7458
7459     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
7460       level->field[x][y] = getMappedElement_DC(element_dc);
7461   }
7462
7463   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
7464   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
7465   level->field[x][y] = EL_PLAYER_1;
7466
7467   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
7468   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
7469   level->field[x][y] = EL_PLAYER_2;
7470
7471   level->gems_needed            = header[18] | (header[19] << 8);
7472
7473   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
7474   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
7475   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
7476   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
7477   level->score[SC_NUT]          = header[28] | (header[29] << 8);
7478   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
7479   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
7480   level->score[SC_BUG]          = header[34] | (header[35] << 8);
7481   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
7482   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
7483   level->score[SC_KEY]          = header[40] | (header[41] << 8);
7484   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
7485
7486   level->time                   = header[44] | (header[45] << 8);
7487
7488   level->amoeba_speed           = header[46] | (header[47] << 8);
7489   level->time_light             = header[48] | (header[49] << 8);
7490   level->time_timegate          = header[50] | (header[51] << 8);
7491   level->time_wheel             = header[52] | (header[53] << 8);
7492   level->time_magic_wall        = header[54] | (header[55] << 8);
7493   level->extra_time             = header[56] | (header[57] << 8);
7494   level->shield_normal_time     = header[58] | (header[59] << 8);
7495
7496   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
7497      can slip down from flat walls, like normal walls and steel walls */
7498   level->em_slippery_gems = TRUE;
7499
7500 #if 0
7501   /* Diamond Caves II levels are always surrounded by indestructible wall, but
7502      not necessarily in a rectangular way -- fill with invisible steel wall */
7503
7504   /* !!! not always true !!! keep level and set BorderElement instead !!! */
7505
7506   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7507   {
7508 #if 1
7509     if ((x == 0 || x == level->fieldx - 1 ||
7510          y == 0 || y == level->fieldy - 1) &&
7511         level->field[x][y] == EL_EMPTY)
7512       level->field[x][y] = EL_INVISIBLE_STEELWALL;
7513 #else
7514     if ((x == 0 || x == level->fieldx - 1 ||
7515          y == 0 || y == level->fieldy - 1) &&
7516         level->field[x][y] == EL_EMPTY)
7517       FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
7518                      level->field, level->fieldx, level->fieldy);
7519 #endif
7520   }
7521 #endif
7522 }
7523
7524 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
7525                                      struct LevelFileInfo *level_file_info,
7526                                      boolean level_info_only)
7527 {
7528   char *filename = level_file_info->filename;
7529   File *file;
7530   int num_magic_bytes = 8;
7531   char magic_bytes[num_magic_bytes + 1];
7532   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7533
7534   if (!(file = openFile(filename, MODE_READ)))
7535   {
7536     level->no_valid_file = TRUE;
7537
7538     if (!level_info_only)
7539       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
7540
7541     return;
7542   }
7543
7544   // fseek(file, 0x0000, SEEK_SET);
7545
7546   if (level_file_info->packed)
7547   {
7548     /* read "magic bytes" from start of file */
7549     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7550       magic_bytes[0] = '\0';
7551
7552     /* check "magic bytes" for correct file format */
7553     if (!strPrefix(magic_bytes, "DC2"))
7554     {
7555       level->no_valid_file = TRUE;
7556
7557       Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
7558             filename);
7559
7560       return;
7561     }
7562
7563     if (strPrefix(magic_bytes, "DC2Win95") ||
7564         strPrefix(magic_bytes, "DC2Win98"))
7565     {
7566       int position_first_level = 0x00fa;
7567       int extra_bytes = 4;
7568       int skip_bytes;
7569
7570       /* advance file stream to first level inside the level package */
7571       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7572
7573       /* each block of level data is followed by block of non-level data */
7574       num_levels_to_skip *= 2;
7575
7576       /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
7577       while (num_levels_to_skip >= 0)
7578       {
7579         /* advance file stream to next level inside the level package */
7580         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7581         {
7582           level->no_valid_file = TRUE;
7583
7584           Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
7585                 filename);
7586
7587           return;
7588         }
7589
7590         /* skip apparently unused extra bytes following each level */
7591         ReadUnusedBytesFromFile(file, extra_bytes);
7592
7593         /* read size of next level in level package */
7594         skip_bytes = getFile32BitLE(file);
7595
7596         num_levels_to_skip--;
7597       }
7598     }
7599     else
7600     {
7601       level->no_valid_file = TRUE;
7602
7603       Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
7604             filename);
7605
7606       return;
7607     }
7608   }
7609
7610   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
7611
7612   closeFile(file);
7613 }
7614
7615 #else
7616
7617 static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
7618                                        int nr)
7619 {
7620   byte header[DC_LEVEL_HEADER_SIZE];
7621   int envelope_size;
7622   int envelope_header_pos = 62;
7623   int envelope_content_pos = 94;
7624   int level_name_pos = 251;
7625   int level_author_pos = 292;
7626   int envelope_header_len;
7627   int envelope_content_len;
7628   int level_name_len;
7629   int level_author_len;
7630   int fieldx, fieldy;
7631   int num_yamyam_contents;
7632   int i, x, y;
7633
7634   getDecodedWord_DC(0, TRUE);           /* initialize DC2 decoding engine */
7635
7636   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
7637   {
7638     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7639
7640     header[i * 2 + 0] = header_word >> 8;
7641     header[i * 2 + 1] = header_word & 0xff;
7642   }
7643
7644   /* read some values from level header to check level decoding integrity */
7645   fieldx = header[6] | (header[7] << 8);
7646   fieldy = header[8] | (header[9] << 8);
7647   num_yamyam_contents = header[60] | (header[61] << 8);
7648
7649   /* do some simple sanity checks to ensure that level was correctly decoded */
7650   if (fieldx < 1 || fieldx > 256 ||
7651       fieldy < 1 || fieldy > 256 ||
7652       num_yamyam_contents < 1 || num_yamyam_contents > 8)
7653   {
7654     level->no_valid_file = TRUE;
7655
7656     Error(ERR_WARN, "cannot decode level from stream -- using empty level");
7657
7658     return;
7659   }
7660
7661   /* maximum envelope header size is 31 bytes */
7662   envelope_header_len   = header[envelope_header_pos];
7663   /* maximum envelope content size is 110 (156?) bytes */
7664   envelope_content_len  = header[envelope_content_pos];
7665
7666   /* maximum level title size is 40 bytes */
7667   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
7668   /* maximum level author size is 30 (51?) bytes */
7669   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
7670
7671   envelope_size = 0;
7672
7673   for (i = 0; i < envelope_header_len; i++)
7674     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7675       level->envelope[0].text[envelope_size++] =
7676         header[envelope_header_pos + 1 + i];
7677
7678   if (envelope_header_len > 0 && envelope_content_len > 0)
7679   {
7680     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7681       level->envelope[0].text[envelope_size++] = '\n';
7682     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7683       level->envelope[0].text[envelope_size++] = '\n';
7684   }
7685
7686   for (i = 0; i < envelope_content_len; i++)
7687     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7688       level->envelope[0].text[envelope_size++] =
7689         header[envelope_content_pos + 1 + i];
7690
7691   level->envelope[0].text[envelope_size] = '\0';
7692
7693   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
7694   level->envelope[0].ysize = 10;
7695   level->envelope[0].autowrap = TRUE;
7696   level->envelope[0].centered = TRUE;
7697
7698   for (i = 0; i < level_name_len; i++)
7699     level->name[i] = header[level_name_pos + 1 + i];
7700   level->name[level_name_len] = '\0';
7701
7702   for (i = 0; i < level_author_len; i++)
7703     level->author[i] = header[level_author_pos + 1 + i];
7704   level->author[level_author_len] = '\0';
7705
7706   num_yamyam_contents = header[60] | (header[61] << 8);
7707   level->num_yamyam_contents =
7708     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
7709
7710   for (i = 0; i < num_yamyam_contents; i++)
7711   {
7712     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
7713     {
7714       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7715 #if 1
7716       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7717 #else
7718       int element_dc = word;
7719 #endif
7720
7721       if (i < MAX_ELEMENT_CONTENTS)
7722         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
7723     }
7724   }
7725
7726   fieldx = header[6] | (header[7] << 8);
7727   fieldy = header[8] | (header[9] << 8);
7728   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
7729   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
7730
7731   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
7732   {
7733     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7734 #if 1
7735     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
7736 #else
7737     int element_dc = word;
7738 #endif
7739
7740     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
7741       level->field[x][y] = getMappedElement_DC(element_dc);
7742   }
7743
7744   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
7745   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
7746   level->field[x][y] = EL_PLAYER_1;
7747
7748   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
7749   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
7750   level->field[x][y] = EL_PLAYER_2;
7751
7752   level->gems_needed            = header[18] | (header[19] << 8);
7753
7754   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
7755   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
7756   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
7757   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
7758   level->score[SC_NUT]          = header[28] | (header[29] << 8);
7759   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
7760   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
7761   level->score[SC_BUG]          = header[34] | (header[35] << 8);
7762   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
7763   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
7764   level->score[SC_KEY]          = header[40] | (header[41] << 8);
7765   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
7766
7767   level->time                   = header[44] | (header[45] << 8);
7768
7769   level->amoeba_speed           = header[46] | (header[47] << 8);
7770   level->time_light             = header[48] | (header[49] << 8);
7771   level->time_timegate          = header[50] | (header[51] << 8);
7772   level->time_wheel             = header[52] | (header[53] << 8);
7773   level->time_magic_wall        = header[54] | (header[55] << 8);
7774   level->extra_time             = header[56] | (header[57] << 8);
7775   level->shield_normal_time     = header[58] | (header[59] << 8);
7776
7777   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
7778      can slip down from flat walls, like normal walls and steel walls */
7779   level->em_slippery_gems = TRUE;
7780
7781 #if 0
7782   /* Diamond Caves II levels are always surrounded by indestructible wall, but
7783      not necessarily in a rectangular way -- fill with invisible steel wall */
7784
7785   /* !!! not always true !!! keep level and set BorderElement instead !!! */
7786
7787   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7788   {
7789 #if 1
7790     if ((x == 0 || x == level->fieldx - 1 ||
7791          y == 0 || y == level->fieldy - 1) &&
7792         level->field[x][y] == EL_EMPTY)
7793       level->field[x][y] = EL_INVISIBLE_STEELWALL;
7794 #else
7795     if ((x == 0 || x == level->fieldx - 1 ||
7796          y == 0 || y == level->fieldy - 1) &&
7797         level->field[x][y] == EL_EMPTY)
7798       FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
7799                      level->field, level->fieldx, level->fieldy);
7800 #endif
7801   }
7802 #endif
7803 }
7804
7805 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
7806                                      struct LevelFileInfo *level_file_info,
7807                                      boolean level_info_only)
7808 {
7809   char *filename = level_file_info->filename;
7810   FILE *file;
7811   int num_magic_bytes = 8;
7812   char magic_bytes[num_magic_bytes + 1];
7813   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7814
7815   if (!(file = fopen(filename, MODE_READ)))
7816   {
7817     level->no_valid_file = TRUE;
7818
7819     if (!level_info_only)
7820       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
7821
7822     return;
7823   }
7824
7825   // fseek(file, 0x0000, SEEK_SET);
7826
7827   if (level_file_info->packed)
7828   {
7829     /* read "magic bytes" from start of file */
7830     if (fgets(magic_bytes, num_magic_bytes + 1, file) == NULL)
7831       magic_bytes[0] = '\0';
7832
7833     /* check "magic bytes" for correct file format */
7834     if (!strPrefix(magic_bytes, "DC2"))
7835     {
7836       level->no_valid_file = TRUE;
7837
7838       Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
7839             filename);
7840
7841       return;
7842     }
7843
7844     if (strPrefix(magic_bytes, "DC2Win95") ||
7845         strPrefix(magic_bytes, "DC2Win98"))
7846     {
7847       int position_first_level = 0x00fa;
7848       int extra_bytes = 4;
7849       int skip_bytes;
7850
7851       /* advance file stream to first level inside the level package */
7852       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7853
7854       /* each block of level data is followed by block of non-level data */
7855       num_levels_to_skip *= 2;
7856
7857       /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
7858       while (num_levels_to_skip >= 0)
7859       {
7860         /* advance file stream to next level inside the level package */
7861         if (fseek(file, skip_bytes, SEEK_CUR) != 0)
7862         {
7863           level->no_valid_file = TRUE;
7864
7865           Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
7866                 filename);
7867
7868           return;
7869         }
7870
7871         /* skip apparently unused extra bytes following each level */
7872         ReadUnusedBytesFromFile(file, extra_bytes);
7873
7874         /* read size of next level in level package */
7875         skip_bytes = getFile32BitLE(file);
7876
7877         num_levels_to_skip--;
7878       }
7879     }
7880     else
7881     {
7882       level->no_valid_file = TRUE;
7883
7884       Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
7885             filename);
7886
7887       return;
7888     }
7889   }
7890
7891   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
7892
7893   fclose(file);
7894 }
7895
7896 #endif
7897
7898 #else
7899
7900 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
7901                                      struct LevelFileInfo *level_file_info)
7902 {
7903   char *filename = level_file_info->filename;
7904   FILE *file;
7905 #if 0
7906   int nr = level_file_info->nr - leveldir_current->first_level;
7907 #endif
7908   byte header[DC_LEVEL_HEADER_SIZE];
7909   int envelope_size;
7910   int envelope_header_pos = 62;
7911   int envelope_content_pos = 94;
7912   int level_name_pos = 251;
7913   int level_author_pos = 292;
7914   int envelope_header_len;
7915   int envelope_content_len;
7916   int level_name_len;
7917   int level_author_len;
7918   int fieldx, fieldy;
7919   int num_yamyam_contents;
7920   int i, x, y;
7921
7922   if (!(file = fopen(filename, MODE_READ)))
7923   {
7924     level->no_valid_file = TRUE;
7925
7926     if (!level_info_only)
7927       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
7928
7929     return;
7930   }
7931
7932 #if 0
7933   /* position file stream to the requested level inside the level package */
7934   if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
7935   {
7936     level->no_valid_file = TRUE;
7937
7938     Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
7939
7940     return;
7941   }
7942 #endif
7943
7944   getDecodedWord_DC(0, TRUE);           /* initialize DC2 decoding engine */
7945
7946   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
7947   {
7948     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
7949
7950     header[i * 2 + 0] = header_word >> 8;
7951     header[i * 2 + 1] = header_word & 0xff;
7952   }
7953
7954   /* read some values from level header to check level decoding integrity */
7955   fieldx = header[6] | (header[7] << 8);
7956   fieldy = header[8] | (header[9] << 8);
7957   num_yamyam_contents = header[60] | (header[61] << 8);
7958
7959   /* do some simple sanity checks to ensure that level was correctly decoded */
7960   if (fieldx < 1 || fieldx > 256 ||
7961       fieldy < 1 || fieldy > 256 ||
7962       num_yamyam_contents < 1 || num_yamyam_contents > 8)
7963   {
7964     level->no_valid_file = TRUE;
7965
7966     Error(ERR_WARN, "cannot read level from file '%s' -- using empty level",
7967           filename);
7968
7969     return;
7970   }
7971
7972   /* maximum envelope header size is 31 bytes */
7973   envelope_header_len   = header[envelope_header_pos];
7974   /* maximum envelope content size is 110 (156?) bytes */
7975   envelope_content_len  = header[envelope_content_pos];
7976
7977   /* maximum level title size is 40 bytes */
7978   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
7979   /* maximum level author size is 30 (51?) bytes */
7980   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
7981
7982   envelope_size = 0;
7983
7984   for (i = 0; i < envelope_header_len; i++)
7985     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7986       level->envelope[0].text[envelope_size++] =
7987         header[envelope_header_pos + 1 + i];
7988
7989   if (envelope_header_len > 0 && envelope_content_len > 0)
7990   {
7991     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7992       level->envelope[0].text[envelope_size++] = '\n';
7993     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7994       level->envelope[0].text[envelope_size++] = '\n';
7995   }
7996
7997   for (i = 0; i < envelope_content_len; i++)
7998     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
7999       level->envelope[0].text[envelope_size++] =
8000         header[envelope_content_pos + 1 + i];
8001
8002   level->envelope[0].text[envelope_size] = '\0';
8003
8004   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
8005   level->envelope[0].ysize = 10;
8006   level->envelope[0].autowrap = TRUE;
8007   level->envelope[0].centered = TRUE;
8008
8009   for (i = 0; i < level_name_len; i++)
8010     level->name[i] = header[level_name_pos + 1 + i];
8011   level->name[level_name_len] = '\0';
8012
8013   for (i = 0; i < level_author_len; i++)
8014     level->author[i] = header[level_author_pos + 1 + i];
8015   level->author[level_author_len] = '\0';
8016
8017   num_yamyam_contents = header[60] | (header[61] << 8);
8018   level->num_yamyam_contents =
8019     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
8020
8021   for (i = 0; i < num_yamyam_contents; i++)
8022   {
8023     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
8024     {
8025       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
8026 #if 1
8027       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
8028 #else
8029       int element_dc = word;
8030 #endif
8031
8032       if (i < MAX_ELEMENT_CONTENTS)
8033         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
8034     }
8035   }
8036
8037   fieldx = header[6] | (header[7] << 8);
8038   fieldy = header[8] | (header[9] << 8);
8039   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
8040   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
8041
8042   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
8043   {
8044     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
8045 #if 1
8046     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
8047 #else
8048     int element_dc = word;
8049 #endif
8050
8051     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
8052       level->field[x][y] = getMappedElement_DC(element_dc);
8053   }
8054
8055   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
8056   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
8057   level->field[x][y] = EL_PLAYER_1;
8058
8059   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
8060   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
8061   level->field[x][y] = EL_PLAYER_2;
8062
8063   level->gems_needed            = header[18] | (header[19] << 8);
8064
8065   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
8066   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
8067   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
8068   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
8069   level->score[SC_NUT]          = header[28] | (header[29] << 8);
8070   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
8071   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
8072   level->score[SC_BUG]          = header[34] | (header[35] << 8);
8073   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
8074   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
8075   level->score[SC_KEY]          = header[40] | (header[41] << 8);
8076   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
8077
8078   level->time                   = header[44] | (header[45] << 8);
8079
8080   level->amoeba_speed           = header[46] | (header[47] << 8);
8081   level->time_light             = header[48] | (header[49] << 8);
8082   level->time_timegate          = header[50] | (header[51] << 8);
8083   level->time_wheel             = header[52] | (header[53] << 8);
8084   level->time_magic_wall        = header[54] | (header[55] << 8);
8085   level->extra_time             = header[56] | (header[57] << 8);
8086   level->shield_normal_time     = header[58] | (header[59] << 8);
8087
8088   fclose(file);
8089
8090   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
8091      can slip down from flat walls, like normal walls and steel walls */
8092   level->em_slippery_gems = TRUE;
8093
8094 #if 0
8095   /* Diamond Caves II levels are always surrounded by indestructible wall, but
8096      not necessarily in a rectangular way -- fill with invisible steel wall */
8097
8098   /* !!! not always true !!! keep level and set BorderElement instead !!! */
8099
8100   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8101   {
8102 #if 1
8103     if ((x == 0 || x == level->fieldx - 1 ||
8104          y == 0 || y == level->fieldy - 1) &&
8105         level->field[x][y] == EL_EMPTY)
8106       level->field[x][y] = EL_INVISIBLE_STEELWALL;
8107 #else
8108     if ((x == 0 || x == level->fieldx - 1 ||
8109          y == 0 || y == level->fieldy - 1) &&
8110         level->field[x][y] == EL_EMPTY)
8111       FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
8112                      level->field, level->fieldx, level->fieldy);
8113 #endif
8114   }
8115 #endif
8116 }
8117
8118 #endif
8119
8120
8121 /* ------------------------------------------------------------------------- */
8122 /* functions for loading SB level                                            */
8123 /* ------------------------------------------------------------------------- */
8124
8125 int getMappedElement_SB(int element_ascii, boolean use_ces)
8126 {
8127   static struct
8128   {
8129     int ascii;
8130     int sb;
8131     int ce;
8132   }
8133   sb_element_mapping[] =
8134   {
8135     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  /* floor (space) */
8136     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  /* wall */
8137     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  /* player */
8138     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  /* box */
8139     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  /* goal square */
8140     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  /* box on goal square */
8141     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  /* player on goal square */
8142 #if 0
8143     { '_', EL_INVISIBLE_STEELWALL,  EL_CUSTOM_8 },  /* floor beyond border */
8144 #else
8145     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  /* floor beyond border */
8146 #endif
8147
8148     { 0,   -1,                      -1          },
8149   };
8150
8151   int i;
8152
8153   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
8154     if (element_ascii == sb_element_mapping[i].ascii)
8155       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
8156
8157   return EL_UNDEFINED;
8158 }
8159
8160 #if 1
8161
8162 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
8163                                      struct LevelFileInfo *level_file_info,
8164                                      boolean level_info_only)
8165 {
8166   char *filename = level_file_info->filename;
8167   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
8168   char last_comment[MAX_LINE_LEN];
8169   char level_name[MAX_LINE_LEN];
8170   char *line_ptr;
8171   File *file;
8172   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
8173   boolean read_continued_line = FALSE;
8174   boolean reading_playfield = FALSE;
8175   boolean got_valid_playfield_line = FALSE;
8176   boolean invalid_playfield_char = FALSE;
8177   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
8178   int file_level_nr = 0;
8179   int line_nr = 0;
8180   int x = 0, y = 0;             /* initialized to make compilers happy */
8181
8182 #if 0
8183   printf("::: looking for level number %d [%d]\n",
8184          level_file_info->nr, num_levels_to_skip);
8185 #endif
8186
8187   last_comment[0] = '\0';
8188   level_name[0] = '\0';
8189
8190   if (!(file = openFile(filename, MODE_READ)))
8191   {
8192     level->no_valid_file = TRUE;
8193
8194     if (!level_info_only)
8195       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8196
8197     return;
8198   }
8199
8200   while (!checkEndOfFile(file))
8201   {
8202     /* level successfully read, but next level may follow here */
8203     if (!got_valid_playfield_line && reading_playfield)
8204     {
8205 #if 0
8206       printf("::: read complete playfield\n");
8207 #endif
8208
8209       /* read playfield from single level file -- skip remaining file */
8210       if (!level_file_info->packed)
8211         break;
8212
8213       if (file_level_nr >= num_levels_to_skip)
8214         break;
8215
8216       file_level_nr++;
8217
8218       last_comment[0] = '\0';
8219       level_name[0] = '\0';
8220
8221       reading_playfield = FALSE;
8222     }
8223
8224     got_valid_playfield_line = FALSE;
8225
8226     /* read next line of input file */
8227     if (!getStringFromFile(file, line, MAX_LINE_LEN))
8228       break;
8229
8230     /* check if line was completely read and is terminated by line break */
8231     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8232       line_nr++;
8233
8234     /* cut trailing line break (this can be newline and/or carriage return) */
8235     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
8236       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
8237         *line_ptr = '\0';
8238
8239     /* copy raw input line for later use (mainly debugging output) */
8240     strcpy(line_raw, line);
8241
8242     if (read_continued_line)
8243     {
8244       /* append new line to existing line, if there is enough space */
8245       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
8246         strcat(previous_line, line_ptr);
8247
8248       strcpy(line, previous_line);      /* copy storage buffer to line */
8249
8250       read_continued_line = FALSE;
8251     }
8252
8253     /* if the last character is '\', continue at next line */
8254     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
8255     {
8256       line[strlen(line) - 1] = '\0';    /* cut off trailing backslash */
8257       strcpy(previous_line, line);      /* copy line to storage buffer */
8258
8259       read_continued_line = TRUE;
8260
8261       continue;
8262     }
8263
8264     /* skip empty lines */
8265     if (line[0] == '\0')
8266       continue;
8267
8268     /* extract comment text from comment line */
8269     if (line[0] == ';')
8270     {
8271       for (line_ptr = line; *line_ptr; line_ptr++)
8272         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
8273           break;
8274
8275       strcpy(last_comment, line_ptr);
8276
8277 #if 0
8278       printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
8279 #endif
8280
8281       continue;
8282     }
8283
8284     /* extract level title text from line containing level title */
8285     if (line[0] == '\'')
8286     {
8287       strcpy(level_name, &line[1]);
8288
8289       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
8290         level_name[strlen(level_name) - 1] = '\0';
8291
8292 #if 0
8293       printf("::: found level name '%s' in line %d\n", level_name, line_nr);
8294 #endif
8295
8296       continue;
8297     }
8298
8299     /* skip lines containing only spaces (or empty lines) */
8300     for (line_ptr = line; *line_ptr; line_ptr++)
8301       if (*line_ptr != ' ')
8302         break;
8303     if (*line_ptr == '\0')
8304       continue;
8305
8306     /* at this point, we have found a line containing part of a playfield */
8307
8308 #if 0
8309     printf("::: found playfield row in line %d\n", line_nr);
8310 #endif
8311
8312     got_valid_playfield_line = TRUE;
8313
8314     if (!reading_playfield)
8315     {
8316       reading_playfield = TRUE;
8317       invalid_playfield_char = FALSE;
8318
8319       for (x = 0; x < MAX_LEV_FIELDX; x++)
8320         for (y = 0; y < MAX_LEV_FIELDY; y++)
8321           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
8322
8323       level->fieldx = 0;
8324       level->fieldy = 0;
8325
8326       /* start with topmost tile row */
8327       y = 0;
8328     }
8329
8330     /* skip playfield line if larger row than allowed */
8331     if (y >= MAX_LEV_FIELDY)
8332       continue;
8333
8334     /* start with leftmost tile column */
8335     x = 0;
8336
8337     /* read playfield elements from line */
8338     for (line_ptr = line; *line_ptr; line_ptr++)
8339     {
8340       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
8341
8342       /* stop parsing playfield line if larger column than allowed */
8343       if (x >= MAX_LEV_FIELDX)
8344         break;
8345
8346       if (mapped_sb_element == EL_UNDEFINED)
8347       {
8348         invalid_playfield_char = TRUE;
8349
8350         break;
8351       }
8352
8353       level->field[x][y] = mapped_sb_element;
8354
8355       /* continue with next tile column */
8356       x++;
8357
8358       level->fieldx = MAX(x, level->fieldx);
8359     }
8360
8361     if (invalid_playfield_char)
8362     {
8363       /* if first playfield line, treat invalid lines as comment lines */
8364       if (y == 0)
8365         reading_playfield = FALSE;
8366
8367       continue;
8368     }
8369
8370     /* continue with next tile row */
8371     y++;
8372   }
8373
8374   closeFile(file);
8375
8376   level->fieldy = y;
8377
8378   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
8379   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
8380
8381   if (!reading_playfield)
8382   {
8383     level->no_valid_file = TRUE;
8384
8385     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8386
8387     return;
8388   }
8389
8390   if (*level_name != '\0')
8391   {
8392     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
8393     level->name[MAX_LEVEL_NAME_LEN] = '\0';
8394
8395 #if 0
8396     printf(":1: level name: '%s'\n", level->name);
8397 #endif
8398   }
8399   else if (*last_comment != '\0')
8400   {
8401     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
8402     level->name[MAX_LEVEL_NAME_LEN] = '\0';
8403
8404 #if 0
8405     printf(":2: level name: '%s'\n", level->name);
8406 #endif
8407   }
8408   else
8409   {
8410     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
8411   }
8412
8413   /* set all empty fields beyond the border walls to invisible steel wall */
8414   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8415   {
8416     if ((x == 0 || x == level->fieldx - 1 ||
8417          y == 0 || y == level->fieldy - 1) &&
8418         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
8419       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
8420                      level->field, level->fieldx, level->fieldy);
8421   }
8422
8423   /* set special level settings for Sokoban levels */
8424
8425   level->time = 0;
8426   level->use_step_counter = TRUE;
8427
8428   if (load_xsb_to_ces)
8429   {
8430 #if 1
8431     /* !!! special global settings can now be set in level template !!! */
8432 #else
8433     level->initial_player_stepsize[0] = STEPSIZE_SLOW;
8434 #endif
8435
8436     /* fill smaller playfields with padding "beyond border wall" elements */
8437     if (level->fieldx < SCR_FIELDX ||
8438         level->fieldy < SCR_FIELDY)
8439     {
8440       short field[level->fieldx][level->fieldy];
8441       int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
8442       int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
8443       int pos_fieldx = (new_fieldx - level->fieldx) / 2;
8444       int pos_fieldy = (new_fieldy - level->fieldy) / 2;
8445
8446       /* copy old playfield (which is smaller than the visible area) */
8447       for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8448         field[x][y] = level->field[x][y];
8449
8450       /* fill new, larger playfield with "beyond border wall" elements */
8451       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
8452         level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
8453
8454       /* copy the old playfield to the middle of the new playfield */
8455       for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8456         level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
8457
8458       level->fieldx = new_fieldx;
8459       level->fieldy = new_fieldy;
8460     }
8461
8462     level->use_custom_template = TRUE;
8463   }
8464 }
8465
8466 #else
8467
8468 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
8469                                      struct LevelFileInfo *level_file_info,
8470                                      boolean level_info_only)
8471 {
8472   char *filename = level_file_info->filename;
8473   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
8474   char last_comment[MAX_LINE_LEN];
8475   char level_name[MAX_LINE_LEN];
8476   char *line_ptr;
8477   FILE *file;
8478   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
8479   boolean read_continued_line = FALSE;
8480   boolean reading_playfield = FALSE;
8481   boolean got_valid_playfield_line = FALSE;
8482   boolean invalid_playfield_char = FALSE;
8483   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
8484   int file_level_nr = 0;
8485   int line_nr = 0;
8486   int x = 0, y = 0;             /* initialized to make compilers happy */
8487
8488 #if 0
8489   printf("::: looking for level number %d [%d]\n",
8490          level_file_info->nr, num_levels_to_skip);
8491 #endif
8492
8493   last_comment[0] = '\0';
8494   level_name[0] = '\0';
8495
8496   if (!(file = fopen(filename, MODE_READ)))
8497   {
8498     level->no_valid_file = TRUE;
8499
8500     if (!level_info_only)
8501       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8502
8503     return;
8504   }
8505
8506   while (!feof(file))
8507   {
8508     /* level successfully read, but next level may follow here */
8509     if (!got_valid_playfield_line && reading_playfield)
8510     {
8511 #if 0
8512       printf("::: read complete playfield\n");
8513 #endif
8514
8515       /* read playfield from single level file -- skip remaining file */
8516       if (!level_file_info->packed)
8517         break;
8518
8519       if (file_level_nr >= num_levels_to_skip)
8520         break;
8521
8522       file_level_nr++;
8523
8524       last_comment[0] = '\0';
8525       level_name[0] = '\0';
8526
8527       reading_playfield = FALSE;
8528     }
8529
8530     got_valid_playfield_line = FALSE;
8531
8532     /* read next line of input file */
8533     if (!fgets(line, MAX_LINE_LEN, file))
8534       break;
8535
8536     /* check if line was completely read and is terminated by line break */
8537     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8538       line_nr++;
8539
8540     /* cut trailing line break (this can be newline and/or carriage return) */
8541     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
8542       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
8543         *line_ptr = '\0';
8544
8545     /* copy raw input line for later use (mainly debugging output) */
8546     strcpy(line_raw, line);
8547
8548     if (read_continued_line)
8549     {
8550       /* append new line to existing line, if there is enough space */
8551       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
8552         strcat(previous_line, line_ptr);
8553
8554       strcpy(line, previous_line);      /* copy storage buffer to line */
8555
8556       read_continued_line = FALSE;
8557     }
8558
8559     /* if the last character is '\', continue at next line */
8560     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
8561     {
8562       line[strlen(line) - 1] = '\0';    /* cut off trailing backslash */
8563       strcpy(previous_line, line);      /* copy line to storage buffer */
8564
8565       read_continued_line = TRUE;
8566
8567       continue;
8568     }
8569
8570     /* skip empty lines */
8571     if (line[0] == '\0')
8572       continue;
8573
8574     /* extract comment text from comment line */
8575     if (line[0] == ';')
8576     {
8577       for (line_ptr = line; *line_ptr; line_ptr++)
8578         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
8579           break;
8580
8581       strcpy(last_comment, line_ptr);
8582
8583 #if 0
8584       printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
8585 #endif
8586
8587       continue;
8588     }
8589
8590     /* extract level title text from line containing level title */
8591     if (line[0] == '\'')
8592     {
8593       strcpy(level_name, &line[1]);
8594
8595       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
8596         level_name[strlen(level_name) - 1] = '\0';
8597
8598 #if 0
8599       printf("::: found level name '%s' in line %d\n", level_name, line_nr);
8600 #endif
8601
8602       continue;
8603     }
8604
8605     /* skip lines containing only spaces (or empty lines) */
8606     for (line_ptr = line; *line_ptr; line_ptr++)
8607       if (*line_ptr != ' ')
8608         break;
8609     if (*line_ptr == '\0')
8610       continue;
8611
8612     /* at this point, we have found a line containing part of a playfield */
8613
8614 #if 0
8615     printf("::: found playfield row in line %d\n", line_nr);
8616 #endif
8617
8618     got_valid_playfield_line = TRUE;
8619
8620     if (!reading_playfield)
8621     {
8622       reading_playfield = TRUE;
8623       invalid_playfield_char = FALSE;
8624
8625       for (x = 0; x < MAX_LEV_FIELDX; x++)
8626         for (y = 0; y < MAX_LEV_FIELDY; y++)
8627           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
8628
8629       level->fieldx = 0;
8630       level->fieldy = 0;
8631
8632       /* start with topmost tile row */
8633       y = 0;
8634     }
8635
8636     /* skip playfield line if larger row than allowed */
8637     if (y >= MAX_LEV_FIELDY)
8638       continue;
8639
8640     /* start with leftmost tile column */
8641     x = 0;
8642
8643     /* read playfield elements from line */
8644     for (line_ptr = line; *line_ptr; line_ptr++)
8645     {
8646       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
8647
8648       /* stop parsing playfield line if larger column than allowed */
8649       if (x >= MAX_LEV_FIELDX)
8650         break;
8651
8652       if (mapped_sb_element == EL_UNDEFINED)
8653       {
8654         invalid_playfield_char = TRUE;
8655
8656         break;
8657       }
8658
8659       level->field[x][y] = mapped_sb_element;
8660
8661       /* continue with next tile column */
8662       x++;
8663
8664       level->fieldx = MAX(x, level->fieldx);
8665     }
8666
8667     if (invalid_playfield_char)
8668     {
8669       /* if first playfield line, treat invalid lines as comment lines */
8670       if (y == 0)
8671         reading_playfield = FALSE;
8672
8673       continue;
8674     }
8675
8676     /* continue with next tile row */
8677     y++;
8678   }
8679
8680   fclose(file);
8681
8682   level->fieldy = y;
8683
8684   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
8685   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
8686
8687   if (!reading_playfield)
8688   {
8689     level->no_valid_file = TRUE;
8690
8691     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
8692
8693     return;
8694   }
8695
8696   if (*level_name != '\0')
8697   {
8698     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
8699     level->name[MAX_LEVEL_NAME_LEN] = '\0';
8700
8701 #if 0
8702     printf(":1: level name: '%s'\n", level->name);
8703 #endif
8704   }
8705   else if (*last_comment != '\0')
8706   {
8707     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
8708     level->name[MAX_LEVEL_NAME_LEN] = '\0';
8709
8710 #if 0
8711     printf(":2: level name: '%s'\n", level->name);
8712 #endif
8713   }
8714   else
8715   {
8716     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
8717   }
8718
8719   /* set all empty fields beyond the border walls to invisible steel wall */
8720   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8721   {
8722     if ((x == 0 || x == level->fieldx - 1 ||
8723          y == 0 || y == level->fieldy - 1) &&
8724         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
8725       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
8726                      level->field, level->fieldx, level->fieldy);
8727   }
8728
8729   /* set special level settings for Sokoban levels */
8730
8731   level->time = 0;
8732   level->use_step_counter = TRUE;
8733
8734   if (load_xsb_to_ces)
8735   {
8736 #if 1
8737     /* !!! special global settings can now be set in level template !!! */
8738 #else
8739     level->initial_player_stepsize[0] = STEPSIZE_SLOW;
8740 #endif
8741
8742     /* fill smaller playfields with padding "beyond border wall" elements */
8743     if (level->fieldx < SCR_FIELDX ||
8744         level->fieldy < SCR_FIELDY)
8745     {
8746       short field[level->fieldx][level->fieldy];
8747       int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
8748       int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
8749       int pos_fieldx = (new_fieldx - level->fieldx) / 2;
8750       int pos_fieldy = (new_fieldy - level->fieldy) / 2;
8751
8752       /* copy old playfield (which is smaller than the visible area) */
8753       for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8754         field[x][y] = level->field[x][y];
8755
8756       /* fill new, larger playfield with "beyond border wall" elements */
8757       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
8758         level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
8759
8760       /* copy the old playfield to the middle of the new playfield */
8761       for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
8762         level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
8763
8764       level->fieldx = new_fieldx;
8765       level->fieldy = new_fieldy;
8766     }
8767
8768     level->use_custom_template = TRUE;
8769   }
8770 }
8771
8772 #endif
8773
8774
8775 /* ------------------------------------------------------------------------- */
8776 /* functions for handling native levels                                      */
8777 /* ------------------------------------------------------------------------- */
8778
8779 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
8780                                      struct LevelFileInfo *level_file_info,
8781                                      boolean level_info_only)
8782 {
8783   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
8784     level->no_valid_file = TRUE;
8785 }
8786
8787 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
8788                                      struct LevelFileInfo *level_file_info,
8789                                      boolean level_info_only)
8790 {
8791   int pos = 0;
8792
8793   /* determine position of requested level inside level package */
8794   if (level_file_info->packed)
8795     pos = level_file_info->nr - leveldir_current->first_level;
8796
8797   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
8798     level->no_valid_file = TRUE;
8799 }
8800
8801 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
8802 {
8803   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
8804     CopyNativeLevel_RND_to_EM(level);
8805   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
8806     CopyNativeLevel_RND_to_SP(level);
8807 }
8808
8809 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
8810 {
8811   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
8812     CopyNativeLevel_EM_to_RND(level);
8813   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
8814     CopyNativeLevel_SP_to_RND(level);
8815 }
8816
8817 void SaveNativeLevel(struct LevelInfo *level)
8818 {
8819   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
8820   {
8821     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
8822     char *filename = getLevelFilenameFromBasename(basename);
8823
8824     CopyNativeLevel_RND_to_SP(level);
8825     CopyNativeTape_RND_to_SP(level);
8826
8827     SaveNativeLevel_SP(filename);
8828   }
8829 }
8830
8831
8832 /* ------------------------------------------------------------------------- */
8833 /* functions for loading generic level                                       */
8834 /* ------------------------------------------------------------------------- */
8835
8836 static void LoadLevelFromFileInfo(struct LevelInfo *level,
8837                                   struct LevelFileInfo *level_file_info,
8838                                   boolean level_info_only)
8839 {
8840   /* always start with reliable default values */
8841   setLevelInfoToDefaults(level, level_info_only);
8842
8843   switch (level_file_info->type)
8844   {
8845     case LEVEL_FILE_TYPE_RND:
8846       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
8847       break;
8848
8849     case LEVEL_FILE_TYPE_EM:
8850       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
8851       level->game_engine_type = GAME_ENGINE_TYPE_EM;
8852       break;
8853
8854     case LEVEL_FILE_TYPE_SP:
8855       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
8856       level->game_engine_type = GAME_ENGINE_TYPE_SP;
8857       break;
8858
8859     case LEVEL_FILE_TYPE_DC:
8860       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
8861       break;
8862
8863     case LEVEL_FILE_TYPE_SB:
8864       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
8865       break;
8866
8867     default:
8868       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
8869       break;
8870   }
8871
8872   /* if level file is invalid, restore level structure to default values */
8873   if (level->no_valid_file)
8874   {
8875     setLevelInfoToDefaults(level, level_info_only);
8876
8877     level->no_valid_file = TRUE;        /* but keep "no valid file" flag */
8878   }
8879
8880   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
8881     level->game_engine_type = GAME_ENGINE_TYPE_RND;
8882
8883   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
8884     CopyNativeLevel_Native_to_RND(level);
8885 }
8886
8887 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
8888 {
8889   static struct LevelFileInfo level_file_info;
8890
8891   /* always start with reliable default values */
8892   setFileInfoToDefaults(&level_file_info);
8893
8894   level_file_info.nr = 0;                       /* unknown level number */
8895   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
8896   level_file_info.filename = filename;
8897
8898   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
8899 }
8900
8901 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
8902 {
8903   int i, j;
8904
8905   if (leveldir_current == NULL)         /* only when dumping level */
8906     return;
8907
8908   /* all engine modifications also valid for levels which use latest engine */
8909   if (level->game_version < VERSION_IDENT(3,2,0,5))
8910   {
8911     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
8912     level->score[SC_TIME_BONUS] /= 10;
8913   }
8914
8915 #if 0
8916   leveldir_current->latest_engine = TRUE;       /* !!! TEST ONLY !!! */
8917 #endif
8918
8919   if (leveldir_current->latest_engine)
8920   {
8921     /* ---------- use latest game engine ----------------------------------- */
8922
8923     /* For all levels which are forced to use the latest game engine version
8924        (normally all but user contributed, private and undefined levels), set
8925        the game engine version to the actual version; this allows for actual
8926        corrections in the game engine to take effect for existing, converted
8927        levels (from "classic" or other existing games) to make the emulation
8928        of the corresponding game more accurate, while (hopefully) not breaking
8929        existing levels created from other players. */
8930
8931     level->game_version = GAME_VERSION_ACTUAL;
8932
8933     /* Set special EM style gems behaviour: EM style gems slip down from
8934        normal, steel and growing wall. As this is a more fundamental change,
8935        it seems better to set the default behaviour to "off" (as it is more
8936        natural) and make it configurable in the level editor (as a property
8937        of gem style elements). Already existing converted levels (neither
8938        private nor contributed levels) are changed to the new behaviour. */
8939
8940     if (level->file_version < FILE_VERSION_2_0)
8941       level->em_slippery_gems = TRUE;
8942
8943     return;
8944   }
8945
8946   /* ---------- use game engine the level was created with ----------------- */
8947
8948   /* For all levels which are not forced to use the latest game engine
8949      version (normally user contributed, private and undefined levels),
8950      use the version of the game engine the levels were created for.
8951
8952      Since 2.0.1, the game engine version is now directly stored
8953      in the level file (chunk "VERS"), so there is no need anymore
8954      to set the game version from the file version (except for old,
8955      pre-2.0 levels, where the game version is still taken from the
8956      file format version used to store the level -- see above). */
8957
8958   /* player was faster than enemies in 1.0.0 and before */
8959   if (level->file_version == FILE_VERSION_1_0)
8960     for (i = 0; i < MAX_PLAYERS; i++)
8961       level->initial_player_stepsize[i] = STEPSIZE_FAST;
8962
8963   /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
8964   if (level->game_version == VERSION_IDENT(2,0,1,0))
8965     level->em_slippery_gems = TRUE;
8966
8967   /* springs could be pushed over pits before (pre-release version) 2.2.0 */
8968   if (level->game_version < VERSION_IDENT(2,2,0,0))
8969     level->use_spring_bug = TRUE;
8970
8971   if (level->game_version < VERSION_IDENT(3,2,0,5))
8972   {
8973     /* time orb caused limited time in endless time levels before 3.2.0-5 */
8974     level->use_time_orb_bug = TRUE;
8975
8976     /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
8977     level->block_snap_field = FALSE;
8978
8979     /* extra time score was same value as time left score before 3.2.0-5 */
8980     level->extra_time_score = level->score[SC_TIME_BONUS];
8981
8982 #if 0
8983     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
8984     level->score[SC_TIME_BONUS] /= 10;
8985 #endif
8986   }
8987
8988   if (level->game_version < VERSION_IDENT(3,2,0,7))
8989   {
8990     /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
8991     level->continuous_snapping = FALSE;
8992   }
8993
8994   /* only few elements were able to actively move into acid before 3.1.0 */
8995   /* trigger settings did not exist before 3.1.0; set to default "any" */
8996   if (level->game_version < VERSION_IDENT(3,1,0,0))
8997   {
8998     /* correct "can move into acid" settings (all zero in old levels) */
8999
9000     level->can_move_into_acid_bits = 0; /* nothing can move into acid */
9001     level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
9002
9003     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
9004     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
9005     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
9006     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
9007
9008     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9009       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
9010
9011     /* correct trigger settings (stored as zero == "none" in old levels) */
9012
9013     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9014     {
9015       int element = EL_CUSTOM_START + i;
9016       struct ElementInfo *ei = &element_info[element];
9017
9018       for (j = 0; j < ei->num_change_pages; j++)
9019       {
9020         struct ElementChangeInfo *change = &ei->change_page[j];
9021
9022         change->trigger_player = CH_PLAYER_ANY;
9023         change->trigger_page = CH_PAGE_ANY;
9024       }
9025     }
9026   }
9027
9028   /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
9029   {
9030     int element = EL_CUSTOM_256;
9031     struct ElementInfo *ei = &element_info[element];
9032     struct ElementChangeInfo *change = &ei->change_page[0];
9033
9034     /* This is needed to fix a problem that was caused by a bugfix in function
9035        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
9036        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
9037        not replace walkable elements, but instead just placed the player on it,
9038        without placing the Sokoban field under the player). Unfortunately, this
9039        breaks "Snake Bite" style levels when the snake is halfway through a door
9040        that just closes (the snake head is still alive and can be moved in this
9041        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
9042        player (without Sokoban element) which then gets killed as designed). */
9043
9044     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
9045          strncmp(ei->description, "pause b4 death", 14) == 0) &&
9046         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
9047       change->target_element = EL_PLAYER_1;
9048   }
9049
9050 #if 1
9051   /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
9052   if (level->game_version < VERSION_IDENT(3,2,5,0))
9053   {
9054     /* This is needed to fix a problem that was caused by a bugfix in function
9055        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
9056        corrects the behaviour when a custom element changes to another custom
9057        element with a higher element number that has change actions defined.
9058        Normally, only one change per frame is allowed for custom elements.
9059        Therefore, it is checked if a custom element already changed in the
9060        current frame; if it did, subsequent changes are suppressed.
9061        Unfortunately, this is only checked for element changes, but not for
9062        change actions, which are still executed. As the function above loops
9063        through all custom elements from lower to higher, an element change
9064        resulting in a lower CE number won't be checked again, while a target
9065        element with a higher number will also be checked, and potential change
9066        actions will get executed for this CE, too (which is wrong), while
9067        further changes are ignored (which is correct). As this bugfix breaks
9068        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
9069        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
9070        behaviour for existing levels and tapes that make use of this bug */
9071
9072     level->use_action_after_change_bug = TRUE;
9073   }
9074 #else
9075   /* !!! THIS DOES NOT FIX "Zelda I" (GRAPHICALLY) AND "Alan's FMV" LEVELS */
9076   /* try to detect and fix "Zelda II" levels, which are broken with 3.2.5 */
9077   {
9078     int element = EL_CUSTOM_16;
9079     struct ElementInfo *ei = &element_info[element];
9080
9081     /* This is needed to fix a problem that was caused by a bugfix in function
9082        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
9083        corrects the behaviour when a custom element changes to another custom
9084        element with a higher element number that has change actions defined.
9085        Normally, only one change per frame is allowed for custom elements.
9086        Therefore, it is checked if a custom element already changed in the
9087        current frame; if it did, subsequent changes are suppressed.
9088        Unfortunately, this is only checked for element changes, but not for
9089        change actions, which are still executed. As the function above loops
9090        through all custom elements from lower to higher, an element change
9091        resulting in a lower CE number won't be checked again, while a target
9092        element with a higher number will also be checked, and potential change
9093        actions will get executed for this CE, too (which is wrong), while
9094        further changes are ignored (which is correct). As this bugfix breaks
9095        Zelda II (but no other levels), allow the previous, incorrect behaviour
9096        for this outstanding level set to not break the game or existing tapes */
9097
9098     if (strncmp(leveldir_current->identifier, "zelda2", 6) == 0 ||
9099         strncmp(ei->description, "scanline - row 1", 16) == 0)
9100       level->use_action_after_change_bug = TRUE;
9101   }
9102 #endif
9103
9104   /* not centering level after relocating player was default only in 3.2.3 */
9105   if (level->game_version == VERSION_IDENT(3,2,3,0))    /* (no pre-releases) */
9106     level->shifted_relocation = TRUE;
9107
9108   /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
9109   if (level->game_version < VERSION_IDENT(3,2,6,0))
9110     level->em_explodes_by_fire = TRUE;
9111 }
9112
9113 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
9114 {
9115   int i, j, x, y;
9116
9117   /* map custom element change events that have changed in newer versions
9118      (these following values were accidentally changed in version 3.0.1)
9119      (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
9120   if (level->game_version <= VERSION_IDENT(3,0,0,0))
9121   {
9122     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9123     {
9124       int element = EL_CUSTOM_START + i;
9125
9126       /* order of checking and copying events to be mapped is important */
9127       /* (do not change the start and end value -- they are constant) */
9128       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
9129       {
9130         if (HAS_CHANGE_EVENT(element, j - 2))
9131         {
9132           SET_CHANGE_EVENT(element, j - 2, FALSE);
9133           SET_CHANGE_EVENT(element, j, TRUE);
9134         }
9135       }
9136
9137       /* order of checking and copying events to be mapped is important */
9138       /* (do not change the start and end value -- they are constant) */
9139       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
9140       {
9141         if (HAS_CHANGE_EVENT(element, j - 1))
9142         {
9143           SET_CHANGE_EVENT(element, j - 1, FALSE);
9144           SET_CHANGE_EVENT(element, j, TRUE);
9145         }
9146       }
9147     }
9148   }
9149
9150   /* initialize "can_change" field for old levels with only one change page */
9151   if (level->game_version <= VERSION_IDENT(3,0,2,0))
9152   {
9153     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9154     {
9155       int element = EL_CUSTOM_START + i;
9156
9157       if (CAN_CHANGE(element))
9158         element_info[element].change->can_change = TRUE;
9159     }
9160   }
9161
9162   /* correct custom element values (for old levels without these options) */
9163   if (level->game_version < VERSION_IDENT(3,1,1,0))
9164   {
9165     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9166     {
9167       int element = EL_CUSTOM_START + i;
9168       struct ElementInfo *ei = &element_info[element];
9169
9170       if (ei->access_direction == MV_NO_DIRECTION)
9171         ei->access_direction = MV_ALL_DIRECTIONS;
9172     }
9173   }
9174
9175   /* correct custom element values (fix invalid values for all versions) */
9176   if (1)
9177   {
9178     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9179     {
9180       int element = EL_CUSTOM_START + i;
9181       struct ElementInfo *ei = &element_info[element];
9182
9183       for (j = 0; j < ei->num_change_pages; j++)
9184       {
9185         struct ElementChangeInfo *change = &ei->change_page[j];
9186
9187         if (change->trigger_player == CH_PLAYER_NONE)
9188           change->trigger_player = CH_PLAYER_ANY;
9189
9190         if (change->trigger_side == CH_SIDE_NONE)
9191           change->trigger_side = CH_SIDE_ANY;
9192       }
9193     }
9194   }
9195
9196   /* initialize "can_explode" field for old levels which did not store this */
9197   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
9198   if (level->game_version <= VERSION_IDENT(3,1,0,0))
9199   {
9200     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9201     {
9202       int element = EL_CUSTOM_START + i;
9203
9204       if (EXPLODES_1X1_OLD(element))
9205         element_info[element].explosion_type = EXPLODES_1X1;
9206
9207       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
9208                                              EXPLODES_SMASHED(element) ||
9209                                              EXPLODES_IMPACT(element)));
9210     }
9211   }
9212
9213   /* correct previously hard-coded move delay values for maze runner style */
9214   if (level->game_version < VERSION_IDENT(3,1,1,0))
9215   {
9216     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9217     {
9218       int element = EL_CUSTOM_START + i;
9219
9220       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
9221       {
9222         /* previously hard-coded and therefore ignored */
9223         element_info[element].move_delay_fixed = 9;
9224         element_info[element].move_delay_random = 0;
9225       }
9226     }
9227   }
9228
9229   /* map elements that have changed in newer versions */
9230   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
9231                                                     level->game_version);
9232   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9233     for (x = 0; x < 3; x++)
9234       for (y = 0; y < 3; y++)
9235         level->yamyam_content[i].e[x][y] =
9236           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
9237                                     level->game_version);
9238
9239   /* initialize element properties for level editor etc. */
9240   InitElementPropertiesEngine(level->game_version);
9241   InitElementPropertiesAfterLoading(level->game_version);
9242   InitElementPropertiesGfxElement();
9243 }
9244
9245 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
9246 {
9247   int x, y;
9248
9249   /* map elements that have changed in newer versions */
9250   for (y = 0; y < level->fieldy; y++)
9251     for (x = 0; x < level->fieldx; x++)
9252       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
9253                                                      level->game_version);
9254
9255   /* copy elements to runtime playfield array */
9256   for (x = 0; x < MAX_LEV_FIELDX; x++)
9257     for (y = 0; y < MAX_LEV_FIELDY; y++)
9258       Feld[x][y] = level->field[x][y];
9259
9260   /* initialize level size variables for faster access */
9261   lev_fieldx = level->fieldx;
9262   lev_fieldy = level->fieldy;
9263
9264   /* determine border element for this level */
9265   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
9266     BorderElement = EL_EMPTY;   /* (in editor, SetBorderElement() is used) */
9267   else
9268     SetBorderElement();
9269 }
9270
9271 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
9272 {
9273   struct LevelFileInfo *level_file_info = &level->file_info;
9274
9275   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
9276     CopyNativeLevel_RND_to_Native(level);
9277 }
9278
9279 void LoadLevelTemplate(int nr)
9280 {
9281   char *filename;
9282
9283   setLevelFileInfo(&level_template.file_info, nr);
9284   filename = level_template.file_info.filename;
9285
9286   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
9287
9288   LoadLevel_InitVersion(&level_template, filename);
9289   LoadLevel_InitElements(&level_template, filename);
9290
9291   ActivateLevelTemplate();
9292 }
9293
9294 void LoadLevel(int nr)
9295 {
9296   char *filename;
9297
9298   setLevelFileInfo(&level.file_info, nr);
9299   filename = level.file_info.filename;
9300
9301   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
9302
9303   if (level.use_custom_template)
9304     LoadLevelTemplate(-1);
9305
9306   LoadLevel_InitVersion(&level, filename);
9307   LoadLevel_InitElements(&level, filename);
9308   LoadLevel_InitPlayfield(&level, filename);
9309
9310   LoadLevel_InitNativeEngines(&level, filename);
9311 }
9312
9313 void LoadLevelInfoOnly(int nr)
9314 {
9315 #if 0
9316   char *filename;
9317 #endif
9318
9319   setLevelFileInfo(&level.file_info, nr);
9320 #if 0
9321   filename = level.file_info.filename;
9322 #endif
9323
9324   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
9325 }
9326
9327 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
9328 {
9329   int chunk_size = 0;
9330
9331   chunk_size += putFileVersion(file, level->file_version);
9332   chunk_size += putFileVersion(file, level->game_version);
9333
9334   return chunk_size;
9335 }
9336
9337 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
9338 {
9339   int chunk_size = 0;
9340
9341   chunk_size += putFile16BitBE(file, level->creation_date.year);
9342   chunk_size += putFile8Bit(file,    level->creation_date.month);
9343   chunk_size += putFile8Bit(file,    level->creation_date.day);
9344
9345   return chunk_size;
9346 }
9347
9348 #if 0
9349 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
9350 {
9351   int i, x, y;
9352
9353   putFile8Bit(file, level->fieldx);
9354   putFile8Bit(file, level->fieldy);
9355
9356   putFile16BitBE(file, level->time);
9357   putFile16BitBE(file, level->gems_needed);
9358
9359   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
9360     putFile8Bit(file, level->name[i]);
9361
9362   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
9363     putFile8Bit(file, level->score[i]);
9364
9365   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
9366     for (y = 0; y < 3; y++)
9367       for (x = 0; x < 3; x++)
9368         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
9369                            level->yamyam_content[i].e[x][y]));
9370   putFile8Bit(file, level->amoeba_speed);
9371   putFile8Bit(file, level->time_magic_wall);
9372   putFile8Bit(file, level->time_wheel);
9373   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
9374                      level->amoeba_content));
9375   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
9376   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
9377   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
9378   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
9379
9380   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
9381
9382   putFile8Bit(file, (level->block_last_field ? 1 : 0));
9383   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
9384   putFile32BitBE(file, level->can_move_into_acid_bits);
9385   putFile8Bit(file, level->dont_collide_with_bits);
9386
9387   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
9388   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
9389
9390   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
9391   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
9392   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
9393
9394   putFile8Bit(file, level->game_engine_type);
9395
9396   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
9397 }
9398 #endif
9399
9400 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
9401 {
9402   int chunk_size = 0;
9403   int i;
9404
9405   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
9406     chunk_size += putFile8Bit(file, level->name[i]);
9407
9408   return chunk_size;
9409 }
9410
9411 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
9412 {
9413   int chunk_size = 0;
9414   int i;
9415
9416   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
9417     chunk_size += putFile8Bit(file, level->author[i]);
9418
9419   return chunk_size;
9420 }
9421
9422 #if 0
9423 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
9424 {
9425   int chunk_size = 0;
9426   int x, y;
9427
9428   for (y = 0; y < level->fieldy; y++) 
9429     for (x = 0; x < level->fieldx; x++) 
9430       if (level->encoding_16bit_field)
9431         chunk_size += putFile16BitBE(file, level->field[x][y]);
9432       else
9433         chunk_size += putFile8Bit(file, level->field[x][y]);
9434
9435   return chunk_size;
9436 }
9437 #endif
9438
9439 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
9440 {
9441   int chunk_size = 0;
9442   int x, y;
9443
9444   for (y = 0; y < level->fieldy; y++) 
9445     for (x = 0; x < level->fieldx; x++) 
9446       chunk_size += putFile16BitBE(file, level->field[x][y]);
9447
9448   return chunk_size;
9449 }
9450
9451 #if 0
9452 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
9453 {
9454   int i, x, y;
9455
9456   putFile8Bit(file, EL_YAMYAM);
9457   putFile8Bit(file, level->num_yamyam_contents);
9458   putFile8Bit(file, 0);
9459   putFile8Bit(file, 0);
9460
9461   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9462     for (y = 0; y < 3; y++)
9463       for (x = 0; x < 3; x++)
9464         if (level->encoding_16bit_field)
9465           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
9466         else
9467           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
9468 }
9469 #endif
9470
9471 #if 0
9472 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
9473 {
9474   int i, x, y;
9475   int num_contents, content_xsize, content_ysize;
9476   int content_array[MAX_ELEMENT_CONTENTS][3][3];
9477
9478   if (element == EL_YAMYAM)
9479   {
9480     num_contents = level->num_yamyam_contents;
9481     content_xsize = 3;
9482     content_ysize = 3;
9483
9484     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9485       for (y = 0; y < 3; y++)
9486         for (x = 0; x < 3; x++)
9487           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
9488   }
9489   else if (element == EL_BD_AMOEBA)
9490   {
9491     num_contents = 1;
9492     content_xsize = 1;
9493     content_ysize = 1;
9494
9495     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9496       for (y = 0; y < 3; y++)
9497         for (x = 0; x < 3; x++)
9498           content_array[i][x][y] = EL_EMPTY;
9499     content_array[0][0][0] = level->amoeba_content;
9500   }
9501   else
9502   {
9503     /* chunk header already written -- write empty chunk data */
9504     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
9505
9506     Error(ERR_WARN, "cannot save content for element '%d'", element);
9507     return;
9508   }
9509
9510   putFile16BitBE(file, element);
9511   putFile8Bit(file, num_contents);
9512   putFile8Bit(file, content_xsize);
9513   putFile8Bit(file, content_ysize);
9514
9515   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
9516
9517   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
9518     for (y = 0; y < 3; y++)
9519       for (x = 0; x < 3; x++)
9520         putFile16BitBE(file, content_array[i][x][y]);
9521 }
9522 #endif
9523
9524 #if 0
9525 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
9526 {
9527   int envelope_nr = element - EL_ENVELOPE_1;
9528   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
9529   int chunk_size = 0;
9530   int i;
9531
9532   chunk_size += putFile16BitBE(file, element);
9533   chunk_size += putFile16BitBE(file, envelope_len);
9534   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
9535   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
9536
9537   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
9538   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
9539
9540   for (i = 0; i < envelope_len; i++)
9541     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
9542
9543   return chunk_size;
9544 }
9545 #endif
9546
9547 #if 0
9548 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
9549                            int num_changed_custom_elements)
9550 {
9551   int i, check = 0;
9552
9553   putFile16BitBE(file, num_changed_custom_elements);
9554
9555   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9556   {
9557     int element = EL_CUSTOM_START + i;
9558
9559     struct ElementInfo *ei = &element_info[element];
9560
9561     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
9562     {
9563       if (check < num_changed_custom_elements)
9564       {
9565         putFile16BitBE(file, element);
9566         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
9567       }
9568
9569       check++;
9570     }
9571   }
9572
9573   if (check != num_changed_custom_elements)     /* should not happen */
9574     Error(ERR_WARN, "inconsistent number of custom element properties");
9575 }
9576 #endif
9577
9578 #if 0
9579 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
9580                            int num_changed_custom_elements)
9581 {
9582   int i, check = 0;
9583
9584   putFile16BitBE(file, num_changed_custom_elements);
9585
9586   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9587   {
9588     int element = EL_CUSTOM_START + i;
9589
9590     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
9591     {
9592       if (check < num_changed_custom_elements)
9593       {
9594         putFile16BitBE(file, element);
9595         putFile16BitBE(file, element_info[element].change->target_element);
9596       }
9597
9598       check++;
9599     }
9600   }
9601
9602   if (check != num_changed_custom_elements)     /* should not happen */
9603     Error(ERR_WARN, "inconsistent number of custom target elements");
9604 }
9605 #endif
9606
9607 #if 0
9608 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
9609                            int num_changed_custom_elements)
9610 {
9611   int i, j, x, y, check = 0;
9612
9613   putFile16BitBE(file, num_changed_custom_elements);
9614
9615   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9616   {
9617     int element = EL_CUSTOM_START + i;
9618     struct ElementInfo *ei = &element_info[element];
9619
9620     if (ei->modified_settings)
9621     {
9622       if (check < num_changed_custom_elements)
9623       {
9624         putFile16BitBE(file, element);
9625
9626         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
9627           putFile8Bit(file, ei->description[j]);
9628
9629         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
9630
9631         /* some free bytes for future properties and padding */
9632         WriteUnusedBytesToFile(file, 7);
9633
9634         putFile8Bit(file, ei->use_gfx_element);
9635         putFile16BitBE(file, ei->gfx_element_initial);
9636
9637         putFile8Bit(file, ei->collect_score_initial);
9638         putFile8Bit(file, ei->collect_count_initial);
9639
9640         putFile16BitBE(file, ei->push_delay_fixed);
9641         putFile16BitBE(file, ei->push_delay_random);
9642         putFile16BitBE(file, ei->move_delay_fixed);
9643         putFile16BitBE(file, ei->move_delay_random);
9644
9645         putFile16BitBE(file, ei->move_pattern);
9646         putFile8Bit(file, ei->move_direction_initial);
9647         putFile8Bit(file, ei->move_stepsize);
9648
9649         for (y = 0; y < 3; y++)
9650           for (x = 0; x < 3; x++)
9651             putFile16BitBE(file, ei->content.e[x][y]);
9652
9653         putFile32BitBE(file, ei->change->events);
9654
9655         putFile16BitBE(file, ei->change->target_element);
9656
9657         putFile16BitBE(file, ei->change->delay_fixed);
9658         putFile16BitBE(file, ei->change->delay_random);
9659         putFile16BitBE(file, ei->change->delay_frames);
9660
9661         putFile16BitBE(file, ei->change->initial_trigger_element);
9662
9663         putFile8Bit(file, ei->change->explode);
9664         putFile8Bit(file, ei->change->use_target_content);
9665         putFile8Bit(file, ei->change->only_if_complete);
9666         putFile8Bit(file, ei->change->use_random_replace);
9667
9668         putFile8Bit(file, ei->change->random_percentage);
9669         putFile8Bit(file, ei->change->replace_when);
9670
9671         for (y = 0; y < 3; y++)
9672           for (x = 0; x < 3; x++)
9673             putFile16BitBE(file, ei->change->content.e[x][y]);
9674
9675         putFile8Bit(file, ei->slippery_type);
9676
9677         /* some free bytes for future properties and padding */
9678         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
9679       }
9680
9681       check++;
9682     }
9683   }
9684
9685   if (check != num_changed_custom_elements)     /* should not happen */
9686     Error(ERR_WARN, "inconsistent number of custom element properties");
9687 }
9688 #endif
9689
9690 #if 0
9691 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
9692 {
9693   struct ElementInfo *ei = &element_info[element];
9694   int i, j, x, y;
9695
9696   /* ---------- custom element base property values (96 bytes) ------------- */
9697
9698   putFile16BitBE(file, element);
9699
9700   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
9701     putFile8Bit(file, ei->description[i]);
9702
9703   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
9704
9705   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
9706
9707   putFile8Bit(file, ei->num_change_pages);
9708
9709   putFile16BitBE(file, ei->ce_value_fixed_initial);
9710   putFile16BitBE(file, ei->ce_value_random_initial);
9711   putFile8Bit(file, ei->use_last_ce_value);
9712
9713   putFile8Bit(file, ei->use_gfx_element);
9714   putFile16BitBE(file, ei->gfx_element_initial);
9715
9716   putFile8Bit(file, ei->collect_score_initial);
9717   putFile8Bit(file, ei->collect_count_initial);
9718
9719   putFile8Bit(file, ei->drop_delay_fixed);
9720   putFile8Bit(file, ei->push_delay_fixed);
9721   putFile8Bit(file, ei->drop_delay_random);
9722   putFile8Bit(file, ei->push_delay_random);
9723   putFile16BitBE(file, ei->move_delay_fixed);
9724   putFile16BitBE(file, ei->move_delay_random);
9725
9726   /* bits 0 - 15 of "move_pattern" ... */
9727   putFile16BitBE(file, ei->move_pattern & 0xffff);
9728   putFile8Bit(file, ei->move_direction_initial);
9729   putFile8Bit(file, ei->move_stepsize);
9730
9731   putFile8Bit(file, ei->slippery_type);
9732
9733   for (y = 0; y < 3; y++)
9734     for (x = 0; x < 3; x++)
9735       putFile16BitBE(file, ei->content.e[x][y]);
9736
9737   putFile16BitBE(file, ei->move_enter_element);
9738   putFile16BitBE(file, ei->move_leave_element);
9739   putFile8Bit(file, ei->move_leave_type);
9740
9741   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
9742   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
9743
9744   putFile8Bit(file, ei->access_direction);
9745
9746   putFile8Bit(file, ei->explosion_delay);
9747   putFile8Bit(file, ei->ignition_delay);
9748   putFile8Bit(file, ei->explosion_type);
9749
9750   /* some free bytes for future custom property values and padding */
9751   WriteUnusedBytesToFile(file, 1);
9752
9753   /* ---------- change page property values (48 bytes) --------------------- */
9754
9755   for (i = 0; i < ei->num_change_pages; i++)
9756   {
9757     struct ElementChangeInfo *change = &ei->change_page[i];
9758     unsigned int event_bits;
9759
9760     /* bits 0 - 31 of "has_event[]" ... */
9761     event_bits = 0;
9762     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
9763       if (change->has_event[j])
9764         event_bits |= (1 << j);
9765     putFile32BitBE(file, event_bits);
9766
9767     putFile16BitBE(file, change->target_element);
9768
9769     putFile16BitBE(file, change->delay_fixed);
9770     putFile16BitBE(file, change->delay_random);
9771     putFile16BitBE(file, change->delay_frames);
9772
9773     putFile16BitBE(file, change->initial_trigger_element);
9774
9775     putFile8Bit(file, change->explode);
9776     putFile8Bit(file, change->use_target_content);
9777     putFile8Bit(file, change->only_if_complete);
9778     putFile8Bit(file, change->use_random_replace);
9779
9780     putFile8Bit(file, change->random_percentage);
9781     putFile8Bit(file, change->replace_when);
9782
9783     for (y = 0; y < 3; y++)
9784       for (x = 0; x < 3; x++)
9785         putFile16BitBE(file, change->target_content.e[x][y]);
9786
9787     putFile8Bit(file, change->can_change);
9788
9789     putFile8Bit(file, change->trigger_side);
9790
9791     putFile8Bit(file, change->trigger_player);
9792     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
9793                        log_2(change->trigger_page)));
9794
9795     putFile8Bit(file, change->has_action);
9796     putFile8Bit(file, change->action_type);
9797     putFile8Bit(file, change->action_mode);
9798     putFile16BitBE(file, change->action_arg);
9799
9800     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
9801     event_bits = 0;
9802     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
9803       if (change->has_event[j])
9804         event_bits |= (1 << (j - 32));
9805     putFile8Bit(file, event_bits);
9806   }
9807 }
9808 #endif
9809
9810 #if 0
9811 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
9812 {
9813   struct ElementInfo *ei = &element_info[element];
9814   struct ElementGroupInfo *group = ei->group;
9815   int i;
9816
9817   putFile16BitBE(file, element);
9818
9819   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
9820     putFile8Bit(file, ei->description[i]);
9821
9822   putFile8Bit(file, group->num_elements);
9823
9824   putFile8Bit(file, ei->use_gfx_element);
9825   putFile16BitBE(file, ei->gfx_element_initial);
9826
9827   putFile8Bit(file, group->choice_mode);
9828
9829   /* some free bytes for future values and padding */
9830   WriteUnusedBytesToFile(file, 3);
9831
9832   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
9833     putFile16BitBE(file, group->element[i]);
9834 }
9835 #endif
9836
9837 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
9838                                 boolean write_element)
9839 {
9840   int save_type = entry->save_type;
9841   int data_type = entry->data_type;
9842   int conf_type = entry->conf_type;
9843   int byte_mask = conf_type & CONF_MASK_BYTES;
9844   int element = entry->element;
9845   int default_value = entry->default_value;
9846   int num_bytes = 0;
9847   boolean modified = FALSE;
9848
9849   if (byte_mask != CONF_MASK_MULTI_BYTES)
9850   {
9851     void *value_ptr = entry->value;
9852     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
9853                  *(int *)value_ptr);
9854
9855     /* check if any settings have been modified before saving them */
9856     if (value != default_value)
9857       modified = TRUE;
9858
9859     /* do not save if explicitly told or if unmodified default settings */
9860     if ((save_type == SAVE_CONF_NEVER) ||
9861         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9862       return 0;
9863
9864     if (write_element)
9865       num_bytes += putFile16BitBE(file, element);
9866
9867     num_bytes += putFile8Bit(file, conf_type);
9868     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
9869                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
9870                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
9871                   0);
9872   }
9873   else if (data_type == TYPE_STRING)
9874   {
9875     char *default_string = entry->default_string;
9876     char *string = (char *)(entry->value);
9877     int string_length = strlen(string);
9878     int i;
9879
9880     /* check if any settings have been modified before saving them */
9881     if (!strEqual(string, default_string))
9882       modified = TRUE;
9883
9884     /* do not save if explicitly told or if unmodified default settings */
9885     if ((save_type == SAVE_CONF_NEVER) ||
9886         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9887       return 0;
9888
9889     if (write_element)
9890       num_bytes += putFile16BitBE(file, element);
9891
9892     num_bytes += putFile8Bit(file, conf_type);
9893     num_bytes += putFile16BitBE(file, string_length);
9894
9895     for (i = 0; i < string_length; i++)
9896       num_bytes += putFile8Bit(file, string[i]);
9897   }
9898   else if (data_type == TYPE_ELEMENT_LIST)
9899   {
9900     int *element_array = (int *)(entry->value);
9901     int num_elements = *(int *)(entry->num_entities);
9902     int i;
9903
9904     /* check if any settings have been modified before saving them */
9905     for (i = 0; i < num_elements; i++)
9906       if (element_array[i] != default_value)
9907         modified = TRUE;
9908
9909     /* do not save if explicitly told or if unmodified default settings */
9910     if ((save_type == SAVE_CONF_NEVER) ||
9911         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9912       return 0;
9913
9914     if (write_element)
9915       num_bytes += putFile16BitBE(file, element);
9916
9917     num_bytes += putFile8Bit(file, conf_type);
9918     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
9919
9920     for (i = 0; i < num_elements; i++)
9921       num_bytes += putFile16BitBE(file, element_array[i]);
9922   }
9923   else if (data_type == TYPE_CONTENT_LIST)
9924   {
9925     struct Content *content = (struct Content *)(entry->value);
9926     int num_contents = *(int *)(entry->num_entities);
9927     int i, x, y;
9928
9929     /* check if any settings have been modified before saving them */
9930     for (i = 0; i < num_contents; i++)
9931       for (y = 0; y < 3; y++)
9932         for (x = 0; x < 3; x++)
9933           if (content[i].e[x][y] != default_value)
9934             modified = TRUE;
9935
9936     /* do not save if explicitly told or if unmodified default settings */
9937     if ((save_type == SAVE_CONF_NEVER) ||
9938         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
9939       return 0;
9940
9941     if (write_element)
9942       num_bytes += putFile16BitBE(file, element);
9943
9944     num_bytes += putFile8Bit(file, conf_type);
9945     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
9946
9947     for (i = 0; i < num_contents; i++)
9948       for (y = 0; y < 3; y++)
9949         for (x = 0; x < 3; x++)
9950           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
9951   }
9952
9953   return num_bytes;
9954 }
9955
9956 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
9957 {
9958   int chunk_size = 0;
9959   int i;
9960
9961   li = *level;          /* copy level data into temporary buffer */
9962
9963   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
9964     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
9965
9966   return chunk_size;
9967 }
9968
9969 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
9970 {
9971   int chunk_size = 0;
9972   int i;
9973
9974   li = *level;          /* copy level data into temporary buffer */
9975
9976   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
9977     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
9978
9979   return chunk_size;
9980 }
9981
9982 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
9983 {
9984   int envelope_nr = element - EL_ENVELOPE_1;
9985   int chunk_size = 0;
9986   int i;
9987
9988   chunk_size += putFile16BitBE(file, element);
9989
9990   /* copy envelope data into temporary buffer */
9991   xx_envelope = level->envelope[envelope_nr];
9992
9993   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
9994     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
9995
9996   return chunk_size;
9997 }
9998
9999 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
10000 {
10001   struct ElementInfo *ei = &element_info[element];
10002   int chunk_size = 0;
10003   int i, j;
10004
10005   chunk_size += putFile16BitBE(file, element);
10006
10007   xx_ei = *ei;          /* copy element data into temporary buffer */
10008
10009   /* set default description string for this specific element */
10010   strcpy(xx_default_description, getDefaultElementDescription(ei));
10011
10012 #if 0
10013   /* set (fixed) number of content areas (may be wrong by broken level file) */
10014   /* (this is now directly corrected for broken level files after loading) */
10015   xx_num_contents = 1;
10016 #endif
10017
10018   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
10019     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
10020
10021   for (i = 0; i < ei->num_change_pages; i++)
10022   {
10023     struct ElementChangeInfo *change = &ei->change_page[i];
10024
10025     xx_current_change_page = i;
10026
10027     xx_change = *change;        /* copy change data into temporary buffer */
10028
10029     resetEventBits();
10030     setEventBitsFromEventFlags(change);
10031
10032     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
10033       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
10034                                          FALSE);
10035   }
10036
10037   return chunk_size;
10038 }
10039
10040 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
10041 {
10042   struct ElementInfo *ei = &element_info[element];
10043   struct ElementGroupInfo *group = ei->group;
10044   int chunk_size = 0;
10045   int i;
10046
10047   chunk_size += putFile16BitBE(file, element);
10048
10049   xx_ei = *ei;          /* copy element data into temporary buffer */
10050   xx_group = *group;    /* copy group data into temporary buffer */
10051
10052   /* set default description string for this specific element */
10053   strcpy(xx_default_description, getDefaultElementDescription(ei));
10054
10055   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
10056     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
10057
10058   return chunk_size;
10059 }
10060
10061 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
10062 {
10063   int chunk_size;
10064   int i;
10065   FILE *file;
10066
10067   if (!(file = fopen(filename, MODE_WRITE)))
10068   {
10069     Error(ERR_WARN, "cannot save level file '%s'", filename);
10070     return;
10071   }
10072
10073   level->file_version = FILE_VERSION_ACTUAL;
10074   level->game_version = GAME_VERSION_ACTUAL;
10075
10076   level->creation_date = getCurrentDate();
10077
10078   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10079   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
10080
10081   chunk_size = SaveLevel_VERS(NULL, level);
10082   putFileChunkBE(file, "VERS", chunk_size);
10083   SaveLevel_VERS(file, level);
10084
10085   chunk_size = SaveLevel_DATE(NULL, level);
10086   putFileChunkBE(file, "DATE", chunk_size);
10087   SaveLevel_DATE(file, level);
10088
10089   chunk_size = SaveLevel_NAME(NULL, level);
10090   putFileChunkBE(file, "NAME", chunk_size);
10091   SaveLevel_NAME(file, level);
10092
10093   chunk_size = SaveLevel_AUTH(NULL, level);
10094   putFileChunkBE(file, "AUTH", chunk_size);
10095   SaveLevel_AUTH(file, level);
10096
10097   chunk_size = SaveLevel_INFO(NULL, level);
10098   putFileChunkBE(file, "INFO", chunk_size);
10099   SaveLevel_INFO(file, level);
10100
10101   chunk_size = SaveLevel_BODY(NULL, level);
10102   putFileChunkBE(file, "BODY", chunk_size);
10103   SaveLevel_BODY(file, level);
10104
10105   chunk_size = SaveLevel_ELEM(NULL, level);
10106   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          /* save if changed */
10107   {
10108     putFileChunkBE(file, "ELEM", chunk_size);
10109     SaveLevel_ELEM(file, level);
10110   }
10111
10112   for (i = 0; i < NUM_ENVELOPES; i++)
10113   {
10114     int element = EL_ENVELOPE_1 + i;
10115
10116     chunk_size = SaveLevel_NOTE(NULL, level, element);
10117     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        /* save if changed */
10118     {
10119       putFileChunkBE(file, "NOTE", chunk_size);
10120       SaveLevel_NOTE(file, level, element);
10121     }
10122   }
10123
10124   /* if not using template level, check for non-default custom/group elements */
10125   if (!level->use_custom_template)
10126   {
10127     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10128     {
10129       int element = EL_CUSTOM_START + i;
10130
10131       chunk_size = SaveLevel_CUSX(NULL, level, element);
10132       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      /* save if changed */
10133       {
10134         putFileChunkBE(file, "CUSX", chunk_size);
10135         SaveLevel_CUSX(file, level, element);
10136       }
10137     }
10138
10139     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10140     {
10141       int element = EL_GROUP_START + i;
10142
10143       chunk_size = SaveLevel_GRPX(NULL, level, element);
10144       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      /* save if changed */
10145       {
10146         putFileChunkBE(file, "GRPX", chunk_size);
10147         SaveLevel_GRPX(file, level, element);
10148       }
10149     }
10150   }
10151
10152   fclose(file);
10153
10154   SetFilePermissions(filename, PERMS_PRIVATE);
10155 }
10156
10157 void SaveLevel(int nr)
10158 {
10159   char *filename = getDefaultLevelFilename(nr);
10160
10161   SaveLevelFromFilename(&level, filename);
10162 }
10163
10164 void SaveLevelTemplate()
10165 {
10166   char *filename = getDefaultLevelFilename(-1);
10167
10168   SaveLevelFromFilename(&level, filename);
10169 }
10170
10171 boolean SaveLevelChecked(int nr)
10172 {
10173   char *filename = getDefaultLevelFilename(nr);
10174   boolean new_level = !fileExists(filename);
10175   boolean level_saved = FALSE;
10176
10177   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
10178   {
10179     SaveLevel(nr);
10180
10181     if (new_level)
10182       Request("Level saved!", REQ_CONFIRM);
10183
10184     level_saved = TRUE;
10185   }
10186
10187   return level_saved;
10188 }
10189
10190 void DumpLevel(struct LevelInfo *level)
10191 {
10192   if (level->no_valid_file)
10193   {
10194     Error(ERR_WARN, "cannot dump -- no valid level file found");
10195
10196     return;
10197   }
10198
10199   printf_line("-", 79);
10200   printf("Level xxx (file version %08d, game version %08d)\n",
10201          level->file_version, level->game_version);
10202   printf_line("-", 79);
10203
10204   printf("Level author: '%s'\n", level->author);
10205   printf("Level title:  '%s'\n", level->name);
10206   printf("\n");
10207   printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
10208   printf("\n");
10209   printf("Level time:  %d seconds\n", level->time);
10210   printf("Gems needed: %d\n", level->gems_needed);
10211   printf("\n");
10212   printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
10213   printf("Time for wheel:      %d seconds\n", level->time_wheel);
10214   printf("Time for light:      %d seconds\n", level->time_light);
10215   printf("Time for timegate:   %d seconds\n", level->time_timegate);
10216   printf("\n");
10217   printf("Amoeba speed: %d\n", level->amoeba_speed);
10218   printf("\n");
10219
10220   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
10221   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
10222   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
10223   printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
10224   printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
10225
10226   printf_line("-", 79);
10227 }
10228
10229
10230 /* ========================================================================= */
10231 /* tape file functions                                                       */
10232 /* ========================================================================= */
10233
10234 static void setTapeInfoToDefaults()
10235 {
10236   int i;
10237
10238   /* always start with reliable default values (empty tape) */
10239   TapeErase();
10240
10241   /* default values (also for pre-1.2 tapes) with only the first player */
10242   tape.player_participates[0] = TRUE;
10243   for (i = 1; i < MAX_PLAYERS; i++)
10244     tape.player_participates[i] = FALSE;
10245
10246   /* at least one (default: the first) player participates in every tape */
10247   tape.num_participating_players = 1;
10248
10249   tape.level_nr = level_nr;
10250   tape.counter = 0;
10251   tape.changed = FALSE;
10252
10253   tape.recording = FALSE;
10254   tape.playing = FALSE;
10255   tape.pausing = FALSE;
10256
10257   tape.no_valid_file = FALSE;
10258 }
10259
10260 #if 1
10261
10262 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
10263 {
10264   tape->file_version = getFileVersion(file);
10265   tape->game_version = getFileVersion(file);
10266
10267   return chunk_size;
10268 }
10269
10270 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
10271 {
10272   int i;
10273
10274   tape->random_seed = getFile32BitBE(file);
10275   tape->date        = getFile32BitBE(file);
10276   tape->length      = getFile32BitBE(file);
10277
10278   /* read header fields that are new since version 1.2 */
10279   if (tape->file_version >= FILE_VERSION_1_2)
10280   {
10281     byte store_participating_players = getFile8Bit(file);
10282     int engine_version;
10283
10284     /* since version 1.2, tapes store which players participate in the tape */
10285     tape->num_participating_players = 0;
10286     for (i = 0; i < MAX_PLAYERS; i++)
10287     {
10288       tape->player_participates[i] = FALSE;
10289
10290       if (store_participating_players & (1 << i))
10291       {
10292         tape->player_participates[i] = TRUE;
10293         tape->num_participating_players++;
10294       }
10295     }
10296
10297     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
10298
10299     engine_version = getFileVersion(file);
10300     if (engine_version > 0)
10301       tape->engine_version = engine_version;
10302     else
10303       tape->engine_version = tape->game_version;
10304   }
10305
10306   return chunk_size;
10307 }
10308
10309 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
10310 {
10311   int level_identifier_size;
10312   int i;
10313
10314   level_identifier_size = getFile16BitBE(file);
10315
10316   tape->level_identifier =
10317     checked_realloc(tape->level_identifier, level_identifier_size);
10318
10319   for (i = 0; i < level_identifier_size; i++)
10320     tape->level_identifier[i] = getFile8Bit(file);
10321
10322   tape->level_nr = getFile16BitBE(file);
10323
10324   chunk_size = 2 + level_identifier_size + 2;
10325
10326   return chunk_size;
10327 }
10328
10329 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
10330 {
10331   int i, j;
10332   int chunk_size_expected =
10333     (tape->num_participating_players + 1) * tape->length;
10334
10335   if (chunk_size_expected != chunk_size)
10336   {
10337     ReadUnusedBytesFromFile(file, chunk_size);
10338     return chunk_size_expected;
10339   }
10340
10341   for (i = 0; i < tape->length; i++)
10342   {
10343     if (i >= MAX_TAPE_LEN)
10344       break;
10345
10346     for (j = 0; j < MAX_PLAYERS; j++)
10347     {
10348       tape->pos[i].action[j] = MV_NONE;
10349
10350       if (tape->player_participates[j])
10351         tape->pos[i].action[j] = getFile8Bit(file);
10352     }
10353
10354     tape->pos[i].delay = getFile8Bit(file);
10355
10356     if (tape->file_version == FILE_VERSION_1_0)
10357     {
10358       /* eliminate possible diagonal moves in old tapes */
10359       /* this is only for backward compatibility */
10360
10361       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
10362       byte action = tape->pos[i].action[0];
10363       int k, num_moves = 0;
10364
10365       for (k = 0; k<4; k++)
10366       {
10367         if (action & joy_dir[k])
10368         {
10369           tape->pos[i + num_moves].action[0] = joy_dir[k];
10370           if (num_moves > 0)
10371             tape->pos[i + num_moves].delay = 0;
10372           num_moves++;
10373         }
10374       }
10375
10376       if (num_moves > 1)
10377       {
10378         num_moves--;
10379         i += num_moves;
10380         tape->length += num_moves;
10381       }
10382     }
10383     else if (tape->file_version < FILE_VERSION_2_0)
10384     {
10385       /* convert pre-2.0 tapes to new tape format */
10386
10387       if (tape->pos[i].delay > 1)
10388       {
10389         /* action part */
10390         tape->pos[i + 1] = tape->pos[i];
10391         tape->pos[i + 1].delay = 1;
10392
10393         /* delay part */
10394         for (j = 0; j < MAX_PLAYERS; j++)
10395           tape->pos[i].action[j] = MV_NONE;
10396         tape->pos[i].delay--;
10397
10398         i++;
10399         tape->length++;
10400       }
10401     }
10402
10403     if (checkEndOfFile(file))
10404       break;
10405   }
10406
10407   if (i != tape->length)
10408     chunk_size = (tape->num_participating_players + 1) * i;
10409
10410   return chunk_size;
10411 }
10412
10413 #else
10414
10415 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
10416 {
10417   tape->file_version = getFileVersion(file);
10418   tape->game_version = getFileVersion(file);
10419
10420   return chunk_size;
10421 }
10422
10423 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
10424 {
10425   int i;
10426
10427   tape->random_seed = getFile32BitBE(file);
10428   tape->date        = getFile32BitBE(file);
10429   tape->length      = getFile32BitBE(file);
10430
10431   /* read header fields that are new since version 1.2 */
10432   if (tape->file_version >= FILE_VERSION_1_2)
10433   {
10434     byte store_participating_players = getFile8Bit(file);
10435     int engine_version;
10436
10437     /* since version 1.2, tapes store which players participate in the tape */
10438     tape->num_participating_players = 0;
10439     for (i = 0; i < MAX_PLAYERS; i++)
10440     {
10441       tape->player_participates[i] = FALSE;
10442
10443       if (store_participating_players & (1 << i))
10444       {
10445         tape->player_participates[i] = TRUE;
10446         tape->num_participating_players++;
10447       }
10448     }
10449
10450     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
10451
10452     engine_version = getFileVersion(file);
10453     if (engine_version > 0)
10454       tape->engine_version = engine_version;
10455     else
10456       tape->engine_version = tape->game_version;
10457   }
10458
10459   return chunk_size;
10460 }
10461
10462 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
10463 {
10464   int level_identifier_size;
10465   int i;
10466
10467   level_identifier_size = getFile16BitBE(file);
10468
10469   tape->level_identifier =
10470     checked_realloc(tape->level_identifier, level_identifier_size);
10471
10472   for (i = 0; i < level_identifier_size; i++)
10473     tape->level_identifier[i] = getFile8Bit(file);
10474
10475   tape->level_nr = getFile16BitBE(file);
10476
10477   chunk_size = 2 + level_identifier_size + 2;
10478
10479   return chunk_size;
10480 }
10481
10482 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
10483 {
10484   int i, j;
10485   int chunk_size_expected =
10486     (tape->num_participating_players + 1) * tape->length;
10487
10488   if (chunk_size_expected != chunk_size)
10489   {
10490     ReadUnusedBytesFromFile(file, chunk_size);
10491     return chunk_size_expected;
10492   }
10493
10494   for (i = 0; i < tape->length; i++)
10495   {
10496     if (i >= MAX_TAPE_LEN)
10497       break;
10498
10499     for (j = 0; j < MAX_PLAYERS; j++)
10500     {
10501       tape->pos[i].action[j] = MV_NONE;
10502
10503       if (tape->player_participates[j])
10504         tape->pos[i].action[j] = getFile8Bit(file);
10505     }
10506
10507     tape->pos[i].delay = getFile8Bit(file);
10508
10509     if (tape->file_version == FILE_VERSION_1_0)
10510     {
10511       /* eliminate possible diagonal moves in old tapes */
10512       /* this is only for backward compatibility */
10513
10514       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
10515       byte action = tape->pos[i].action[0];
10516       int k, num_moves = 0;
10517
10518       for (k = 0; k<4; k++)
10519       {
10520         if (action & joy_dir[k])
10521         {
10522           tape->pos[i + num_moves].action[0] = joy_dir[k];
10523           if (num_moves > 0)
10524             tape->pos[i + num_moves].delay = 0;
10525           num_moves++;
10526         }
10527       }
10528
10529       if (num_moves > 1)
10530       {
10531         num_moves--;
10532         i += num_moves;
10533         tape->length += num_moves;
10534       }
10535     }
10536     else if (tape->file_version < FILE_VERSION_2_0)
10537     {
10538       /* convert pre-2.0 tapes to new tape format */
10539
10540       if (tape->pos[i].delay > 1)
10541       {
10542         /* action part */
10543         tape->pos[i + 1] = tape->pos[i];
10544         tape->pos[i + 1].delay = 1;
10545
10546         /* delay part */
10547         for (j = 0; j < MAX_PLAYERS; j++)
10548           tape->pos[i].action[j] = MV_NONE;
10549         tape->pos[i].delay--;
10550
10551         i++;
10552         tape->length++;
10553       }
10554     }
10555
10556     if (feof(file))
10557       break;
10558   }
10559
10560   if (i != tape->length)
10561     chunk_size = (tape->num_participating_players + 1) * i;
10562
10563   return chunk_size;
10564 }
10565
10566 #endif
10567
10568 #if 1
10569
10570 void LoadTape_SokobanSolution(char *filename)
10571 {
10572   File *file;
10573   int move_delay = TILESIZE / level.initial_player_stepsize[0];
10574
10575   if (!(file = openFile(filename, MODE_READ)))
10576   {
10577     tape.no_valid_file = TRUE;
10578
10579     return;
10580   }
10581
10582   while (!checkEndOfFile(file))
10583   {
10584     unsigned char c = getByteFromFile(file);
10585
10586     if (checkEndOfFile(file))
10587       break;
10588
10589     switch (c)
10590     {
10591       case 'u':
10592       case 'U':
10593         tape.pos[tape.length].action[0] = MV_UP;
10594         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10595         tape.length++;
10596         break;
10597
10598       case 'd':
10599       case 'D':
10600         tape.pos[tape.length].action[0] = MV_DOWN;
10601         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10602         tape.length++;
10603         break;
10604
10605       case 'l':
10606       case 'L':
10607         tape.pos[tape.length].action[0] = MV_LEFT;
10608         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10609         tape.length++;
10610         break;
10611
10612       case 'r':
10613       case 'R':
10614         tape.pos[tape.length].action[0] = MV_RIGHT;
10615         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10616         tape.length++;
10617         break;
10618
10619       case '\n':
10620       case '\r':
10621       case '\t':
10622       case ' ':
10623         /* ignore white-space characters */
10624         break;
10625
10626       default:
10627         tape.no_valid_file = TRUE;
10628
10629         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
10630
10631         break;
10632     }
10633   }
10634
10635   closeFile(file);
10636
10637   if (tape.no_valid_file)
10638     return;
10639
10640   tape.length_seconds = GetTapeLength();
10641 }
10642
10643 #else
10644
10645 void LoadTape_SokobanSolution(char *filename)
10646 {
10647   FILE *file;
10648   int move_delay = TILESIZE / level.initial_player_stepsize[0];
10649
10650   if (!(file = fopen(filename, MODE_READ)))
10651   {
10652     tape.no_valid_file = TRUE;
10653
10654     return;
10655   }
10656
10657   while (!feof(file))
10658   {
10659     unsigned char c = fgetc(file);
10660
10661     if (feof(file))
10662       break;
10663
10664     switch (c)
10665     {
10666       case 'u':
10667       case 'U':
10668         tape.pos[tape.length].action[0] = MV_UP;
10669         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10670         tape.length++;
10671         break;
10672
10673       case 'd':
10674       case 'D':
10675         tape.pos[tape.length].action[0] = MV_DOWN;
10676         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10677         tape.length++;
10678         break;
10679
10680       case 'l':
10681       case 'L':
10682         tape.pos[tape.length].action[0] = MV_LEFT;
10683         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10684         tape.length++;
10685         break;
10686
10687       case 'r':
10688       case 'R':
10689         tape.pos[tape.length].action[0] = MV_RIGHT;
10690         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
10691         tape.length++;
10692         break;
10693
10694       case '\n':
10695       case '\r':
10696       case '\t':
10697       case ' ':
10698         /* ignore white-space characters */
10699         break;
10700
10701       default:
10702         tape.no_valid_file = TRUE;
10703
10704         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
10705
10706         break;
10707     }
10708   }
10709
10710   fclose(file);
10711
10712   if (tape.no_valid_file)
10713     return;
10714
10715   tape.length_seconds = GetTapeLength();
10716 }
10717
10718 #endif
10719
10720 #if 1
10721
10722 void LoadTapeFromFilename(char *filename)
10723 {
10724   char cookie[MAX_LINE_LEN];
10725   char chunk_name[CHUNK_ID_LEN + 1];
10726   File *file;
10727   int chunk_size;
10728
10729   /* always start with reliable default values */
10730   setTapeInfoToDefaults();
10731
10732   if (strSuffix(filename, ".sln"))
10733   {
10734     LoadTape_SokobanSolution(filename);
10735
10736     return;
10737   }
10738
10739   if (!(file = openFile(filename, MODE_READ)))
10740   {
10741     tape.no_valid_file = TRUE;
10742
10743     return;
10744   }
10745
10746   getFileChunkBE(file, chunk_name, NULL);
10747   if (strEqual(chunk_name, "RND1"))
10748   {
10749     getFile32BitBE(file);               /* not used */
10750
10751     getFileChunkBE(file, chunk_name, NULL);
10752     if (!strEqual(chunk_name, "TAPE"))
10753     {
10754       tape.no_valid_file = TRUE;
10755
10756       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10757
10758       closeFile(file);
10759
10760       return;
10761     }
10762   }
10763   else  /* check for pre-2.0 file format with cookie string */
10764   {
10765     strcpy(cookie, chunk_name);
10766     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10767       cookie[4] = '\0';
10768     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10769       cookie[strlen(cookie) - 1] = '\0';
10770
10771     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
10772     {
10773       tape.no_valid_file = TRUE;
10774
10775       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10776
10777       closeFile(file);
10778
10779       return;
10780     }
10781
10782     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
10783     {
10784       tape.no_valid_file = TRUE;
10785
10786       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
10787
10788       closeFile(file);
10789
10790       return;
10791     }
10792
10793     /* pre-2.0 tape files have no game version, so use file version here */
10794     tape.game_version = tape.file_version;
10795   }
10796
10797   if (tape.file_version < FILE_VERSION_1_2)
10798   {
10799     /* tape files from versions before 1.2.0 without chunk structure */
10800     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
10801     LoadTape_BODY(file, 2 * tape.length,      &tape);
10802   }
10803   else
10804   {
10805     static struct
10806     {
10807       char *name;
10808       int size;
10809       int (*loader)(File *, int, struct TapeInfo *);
10810     }
10811     chunk_info[] =
10812     {
10813       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
10814       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
10815       { "INFO", -1,                     LoadTape_INFO },
10816       { "BODY", -1,                     LoadTape_BODY },
10817       {  NULL,  0,                      NULL }
10818     };
10819
10820     while (getFileChunkBE(file, chunk_name, &chunk_size))
10821     {
10822       int i = 0;
10823
10824       while (chunk_info[i].name != NULL &&
10825              !strEqual(chunk_name, chunk_info[i].name))
10826         i++;
10827
10828       if (chunk_info[i].name == NULL)
10829       {
10830         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
10831               chunk_name, filename);
10832         ReadUnusedBytesFromFile(file, chunk_size);
10833       }
10834       else if (chunk_info[i].size != -1 &&
10835                chunk_info[i].size != chunk_size)
10836       {
10837         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10838               chunk_size, chunk_name, filename);
10839         ReadUnusedBytesFromFile(file, chunk_size);
10840       }
10841       else
10842       {
10843         /* call function to load this tape chunk */
10844         int chunk_size_expected =
10845           (chunk_info[i].loader)(file, chunk_size, &tape);
10846
10847         /* the size of some chunks cannot be checked before reading other
10848            chunks first (like "HEAD" and "BODY") that contain some header
10849            information, so check them here */
10850         if (chunk_size_expected != chunk_size)
10851         {
10852           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10853                 chunk_size, chunk_name, filename);
10854         }
10855       }
10856     }
10857   }
10858
10859   closeFile(file);
10860
10861   tape.length_seconds = GetTapeLength();
10862
10863 #if 0
10864   printf("::: tape file version: %d\n", tape.file_version);
10865   printf("::: tape game version: %d\n", tape.game_version);
10866   printf("::: tape engine version: %d\n", tape.engine_version);
10867 #endif
10868 }
10869
10870 #else
10871
10872 void LoadTapeFromFilename(char *filename)
10873 {
10874   char cookie[MAX_LINE_LEN];
10875   char chunk_name[CHUNK_ID_LEN + 1];
10876   FILE *file;
10877   int chunk_size;
10878
10879   /* always start with reliable default values */
10880   setTapeInfoToDefaults();
10881
10882   if (strSuffix(filename, ".sln"))
10883   {
10884     LoadTape_SokobanSolution(filename);
10885
10886     return;
10887   }
10888
10889   if (!(file = fopen(filename, MODE_READ)))
10890   {
10891     tape.no_valid_file = TRUE;
10892
10893     return;
10894   }
10895
10896   getFileChunkBE(file, chunk_name, NULL);
10897   if (strEqual(chunk_name, "RND1"))
10898   {
10899     getFile32BitBE(file);               /* not used */
10900
10901     getFileChunkBE(file, chunk_name, NULL);
10902     if (!strEqual(chunk_name, "TAPE"))
10903     {
10904       tape.no_valid_file = TRUE;
10905
10906       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10907       fclose(file);
10908       return;
10909     }
10910   }
10911   else  /* check for pre-2.0 file format with cookie string */
10912   {
10913     strcpy(cookie, chunk_name);
10914     if (fgets(&cookie[4], MAX_LINE_LEN - 4, file) == NULL)
10915       cookie[4] = '\0';
10916     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10917       cookie[strlen(cookie) - 1] = '\0';
10918
10919     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
10920     {
10921       tape.no_valid_file = TRUE;
10922
10923       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
10924       fclose(file);
10925       return;
10926     }
10927
10928     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
10929     {
10930       tape.no_valid_file = TRUE;
10931
10932       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
10933       fclose(file);
10934
10935       return;
10936     }
10937
10938     /* pre-2.0 tape files have no game version, so use file version here */
10939     tape.game_version = tape.file_version;
10940   }
10941
10942   if (tape.file_version < FILE_VERSION_1_2)
10943   {
10944     /* tape files from versions before 1.2.0 without chunk structure */
10945     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
10946     LoadTape_BODY(file, 2 * tape.length,      &tape);
10947   }
10948   else
10949   {
10950     static struct
10951     {
10952       char *name;
10953       int size;
10954       int (*loader)(File *, int, struct TapeInfo *);
10955     }
10956     chunk_info[] =
10957     {
10958       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
10959       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
10960       { "INFO", -1,                     LoadTape_INFO },
10961       { "BODY", -1,                     LoadTape_BODY },
10962       {  NULL,  0,                      NULL }
10963     };
10964
10965     while (getFileChunkBE(file, chunk_name, &chunk_size))
10966     {
10967       int i = 0;
10968
10969       while (chunk_info[i].name != NULL &&
10970              !strEqual(chunk_name, chunk_info[i].name))
10971         i++;
10972
10973       if (chunk_info[i].name == NULL)
10974       {
10975         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
10976               chunk_name, filename);
10977         ReadUnusedBytesFromFile(file, chunk_size);
10978       }
10979       else if (chunk_info[i].size != -1 &&
10980                chunk_info[i].size != chunk_size)
10981       {
10982         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10983               chunk_size, chunk_name, filename);
10984         ReadUnusedBytesFromFile(file, chunk_size);
10985       }
10986       else
10987       {
10988         /* call function to load this tape chunk */
10989         int chunk_size_expected =
10990           (chunk_info[i].loader)(file, chunk_size, &tape);
10991
10992         /* the size of some chunks cannot be checked before reading other
10993            chunks first (like "HEAD" and "BODY") that contain some header
10994            information, so check them here */
10995         if (chunk_size_expected != chunk_size)
10996         {
10997           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
10998                 chunk_size, chunk_name, filename);
10999         }
11000       }
11001     }
11002   }
11003
11004   fclose(file);
11005
11006   tape.length_seconds = GetTapeLength();
11007
11008 #if 0
11009   printf("::: tape file version: %d\n", tape.file_version);
11010   printf("::: tape game version: %d\n", tape.game_version);
11011   printf("::: tape engine version: %d\n", tape.engine_version);
11012 #endif
11013 }
11014
11015 #endif
11016
11017 void LoadTape(int nr)
11018 {
11019   char *filename = getTapeFilename(nr);
11020
11021   LoadTapeFromFilename(filename);
11022 }
11023
11024 void LoadSolutionTape(int nr)
11025 {
11026   char *filename = getSolutionTapeFilename(nr);
11027
11028   LoadTapeFromFilename(filename);
11029
11030 #if 1
11031   if (TAPE_IS_EMPTY(tape) &&
11032       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
11033       level.native_sp_level->demo.is_available)
11034     CopyNativeTape_SP_to_RND(&level);
11035 #endif
11036 }
11037
11038 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
11039 {
11040   putFileVersion(file, tape->file_version);
11041   putFileVersion(file, tape->game_version);
11042 }
11043
11044 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
11045 {
11046   int i;
11047   byte store_participating_players = 0;
11048
11049   /* set bits for participating players for compact storage */
11050   for (i = 0; i < MAX_PLAYERS; i++)
11051     if (tape->player_participates[i])
11052       store_participating_players |= (1 << i);
11053
11054   putFile32BitBE(file, tape->random_seed);
11055   putFile32BitBE(file, tape->date);
11056   putFile32BitBE(file, tape->length);
11057
11058   putFile8Bit(file, store_participating_players);
11059
11060   /* unused bytes not at the end here for 4-byte alignment of engine_version */
11061   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
11062
11063   putFileVersion(file, tape->engine_version);
11064 }
11065
11066 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
11067 {
11068   int level_identifier_size = strlen(tape->level_identifier) + 1;
11069   int i;
11070
11071   putFile16BitBE(file, level_identifier_size);
11072
11073   for (i = 0; i < level_identifier_size; i++)
11074     putFile8Bit(file, tape->level_identifier[i]);
11075
11076   putFile16BitBE(file, tape->level_nr);
11077 }
11078
11079 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
11080 {
11081   int i, j;
11082
11083   for (i = 0; i < tape->length; i++)
11084   {
11085     for (j = 0; j < MAX_PLAYERS; j++)
11086       if (tape->player_participates[j])
11087         putFile8Bit(file, tape->pos[i].action[j]);
11088
11089     putFile8Bit(file, tape->pos[i].delay);
11090   }
11091 }
11092
11093 void SaveTape(int nr)
11094 {
11095   char *filename = getTapeFilename(nr);
11096   FILE *file;
11097 #if 0
11098   boolean new_tape = TRUE;
11099 #endif
11100   int num_participating_players = 0;
11101   int info_chunk_size;
11102   int body_chunk_size;
11103   int i;
11104
11105   InitTapeDirectory(leveldir_current->subdir);
11106
11107 #if 0
11108   /* if a tape still exists, ask to overwrite it */
11109   if (fileExists(filename))
11110   {
11111     new_tape = FALSE;
11112     if (!Request("Replace old tape?", REQ_ASK))
11113       return;
11114   }
11115 #endif
11116
11117   if (!(file = fopen(filename, MODE_WRITE)))
11118   {
11119     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
11120     return;
11121   }
11122
11123   tape.file_version = FILE_VERSION_ACTUAL;
11124   tape.game_version = GAME_VERSION_ACTUAL;
11125
11126   /* count number of participating players  */
11127   for (i = 0; i < MAX_PLAYERS; i++)
11128     if (tape.player_participates[i])
11129       num_participating_players++;
11130
11131   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
11132   body_chunk_size = (num_participating_players + 1) * tape.length;
11133
11134   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
11135   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
11136
11137   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
11138   SaveTape_VERS(file, &tape);
11139
11140   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
11141   SaveTape_HEAD(file, &tape);
11142
11143   putFileChunkBE(file, "INFO", info_chunk_size);
11144   SaveTape_INFO(file, &tape);
11145
11146   putFileChunkBE(file, "BODY", body_chunk_size);
11147   SaveTape_BODY(file, &tape);
11148
11149   fclose(file);
11150
11151   SetFilePermissions(filename, PERMS_PRIVATE);
11152
11153   tape.changed = FALSE;
11154
11155 #if 0
11156   if (new_tape)
11157     Request("Tape saved!", REQ_CONFIRM);
11158 #endif
11159 }
11160
11161 boolean SaveTapeChecked(int nr)
11162 {
11163   char *filename = getTapeFilename(nr);
11164   boolean new_tape = !fileExists(filename);
11165   boolean tape_saved = FALSE;
11166
11167   if (new_tape || Request("Replace old tape?", REQ_ASK))
11168   {
11169     SaveTape(nr);
11170
11171     if (new_tape)
11172       Request("Tape saved!", REQ_CONFIRM);
11173
11174     tape_saved = TRUE;
11175   }
11176
11177   return tape_saved;
11178 }
11179
11180 void DumpTape(struct TapeInfo *tape)
11181 {
11182   int tape_frame_counter;
11183   int i, j;
11184
11185   if (tape->no_valid_file)
11186   {
11187     Error(ERR_WARN, "cannot dump -- no valid tape file found");
11188
11189     return;
11190   }
11191
11192   printf_line("-", 79);
11193   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
11194          tape->level_nr, tape->file_version, tape->game_version);
11195   printf("                  (effective engine version %08d)\n",
11196          tape->engine_version);
11197   printf("Level series identifier: '%s'\n", tape->level_identifier);
11198   printf_line("-", 79);
11199
11200   tape_frame_counter = 0;
11201
11202   for (i = 0; i < tape->length; i++)
11203   {
11204     if (i >= MAX_TAPE_LEN)
11205       break;
11206
11207     printf("%04d: ", i);
11208
11209     for (j = 0; j < MAX_PLAYERS; j++)
11210     {
11211       if (tape->player_participates[j])
11212       {
11213         int action = tape->pos[i].action[j];
11214
11215         printf("%d:%02x ", j, action);
11216         printf("[%c%c%c%c|%c%c] - ",
11217                (action & JOY_LEFT ? '<' : ' '),
11218                (action & JOY_RIGHT ? '>' : ' '),
11219                (action & JOY_UP ? '^' : ' '),
11220                (action & JOY_DOWN ? 'v' : ' '),
11221                (action & JOY_BUTTON_1 ? '1' : ' '),
11222                (action & JOY_BUTTON_2 ? '2' : ' '));
11223       }
11224     }
11225
11226     printf("(%03d) ", tape->pos[i].delay);
11227     printf("[%05d]\n", tape_frame_counter);
11228
11229     tape_frame_counter += tape->pos[i].delay;
11230   }
11231
11232   printf_line("-", 79);
11233 }
11234
11235
11236 /* ========================================================================= */
11237 /* score file functions                                                      */
11238 /* ========================================================================= */
11239
11240 void LoadScore(int nr)
11241 {
11242   int i;
11243   char *filename = getScoreFilename(nr);
11244   char cookie[MAX_LINE_LEN];
11245   char line[MAX_LINE_LEN];
11246   char *line_ptr;
11247   FILE *file;
11248
11249   /* always start with reliable default values */
11250   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
11251   {
11252     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
11253     highscore[i].Score = 0;
11254   }
11255
11256   if (!(file = fopen(filename, MODE_READ)))
11257     return;
11258
11259   /* check file identifier */
11260   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
11261     cookie[0] = '\0';
11262   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
11263     cookie[strlen(cookie) - 1] = '\0';
11264
11265   if (!checkCookieString(cookie, SCORE_COOKIE))
11266   {
11267     Error(ERR_WARN, "unknown format of score file '%s'", filename);
11268     fclose(file);
11269     return;
11270   }
11271
11272   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
11273   {
11274     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
11275       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
11276     if (fgets(line, MAX_LINE_LEN, file) == NULL)
11277       line[0] = '\0';
11278
11279     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
11280       line[strlen(line) - 1] = '\0';
11281
11282     for (line_ptr = line; *line_ptr; line_ptr++)
11283     {
11284       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
11285       {
11286         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
11287         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
11288         break;
11289       }
11290     }
11291   }
11292
11293   fclose(file);
11294 }
11295
11296 void SaveScore(int nr)
11297 {
11298   int i;
11299   char *filename = getScoreFilename(nr);
11300   FILE *file;
11301
11302   InitScoreDirectory(leveldir_current->subdir);
11303
11304   if (!(file = fopen(filename, MODE_WRITE)))
11305   {
11306     Error(ERR_WARN, "cannot save score for level %d", nr);
11307     return;
11308   }
11309
11310   fprintf(file, "%s\n\n", SCORE_COOKIE);
11311
11312   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
11313     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
11314
11315   fclose(file);
11316
11317   SetFilePermissions(filename, PERMS_PUBLIC);
11318 }
11319
11320
11321 /* ========================================================================= */
11322 /* setup file functions                                                      */
11323 /* ========================================================================= */
11324
11325 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
11326
11327 /* global setup */
11328 #define SETUP_TOKEN_PLAYER_NAME                 0
11329 #define SETUP_TOKEN_SOUND                       1
11330 #define SETUP_TOKEN_SOUND_LOOPS                 2
11331 #define SETUP_TOKEN_SOUND_MUSIC                 3
11332 #define SETUP_TOKEN_SOUND_SIMPLE                4
11333 #define SETUP_TOKEN_TOONS                       5
11334 #define SETUP_TOKEN_SCROLL_DELAY                6
11335 #define SETUP_TOKEN_SCROLL_DELAY_VALUE          7
11336 #define SETUP_TOKEN_SOFT_SCROLLING              8
11337 #define SETUP_TOKEN_FADE_SCREENS                9
11338 #define SETUP_TOKEN_AUTORECORD                  10
11339 #define SETUP_TOKEN_SHOW_TITLESCREEN            11
11340 #define SETUP_TOKEN_QUICK_DOORS                 12
11341 #define SETUP_TOKEN_TEAM_MODE                   13
11342 #define SETUP_TOKEN_HANDICAP                    14
11343 #define SETUP_TOKEN_SKIP_LEVELS                 15
11344 #define SETUP_TOKEN_TIME_LIMIT                  16
11345 #define SETUP_TOKEN_FULLSCREEN                  17
11346 #define SETUP_TOKEN_FULLSCREEN_MODE             18
11347 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT      19
11348 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY      20
11349 #define SETUP_TOKEN_ASK_ON_ESCAPE               21
11350 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        22
11351 #define SETUP_TOKEN_QUICK_SWITCH                23
11352 #define SETUP_TOKEN_INPUT_ON_FOCUS              24
11353 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         25
11354 #define SETUP_TOKEN_GAME_FRAME_DELAY            26
11355 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS     27
11356 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS         28
11357 #define SETUP_TOKEN_GRAPHICS_SET                29
11358 #define SETUP_TOKEN_SOUNDS_SET                  30
11359 #define SETUP_TOKEN_MUSIC_SET                   31
11360 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     32
11361 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       33
11362 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        34
11363 #define SETUP_TOKEN_VOLUME_SIMPLE               35
11364 #define SETUP_TOKEN_VOLUME_LOOPS                36
11365 #define SETUP_TOKEN_VOLUME_MUSIC                37
11366 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE          38
11367 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE         39
11368 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE         40
11369
11370 #define NUM_GLOBAL_SETUP_TOKENS                 41
11371
11372 /* editor setup */
11373 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
11374 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
11375 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
11376 #define SETUP_TOKEN_EDITOR_EL_MORE              3
11377 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           4
11378 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          5
11379 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     6
11380 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    7
11381 #define SETUP_TOKEN_EDITOR_EL_CHARS             8
11382 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS       9
11383 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            10
11384 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         11
11385 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      12
11386 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           13
11387 #define SETUP_TOKEN_EDITOR_EL_BY_GAME           14
11388 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE           15
11389 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   16
11390
11391 #define NUM_EDITOR_SETUP_TOKENS                 17
11392
11393 /* editor cascade setup */
11394 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
11395 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
11396 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
11397 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
11398 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
11399 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
11400 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
11401 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
11402 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
11403 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT    9
11404 #define SETUP_TOKEN_EDITOR_CASCADE_CE           10
11405 #define SETUP_TOKEN_EDITOR_CASCADE_GE           11
11406 #define SETUP_TOKEN_EDITOR_CASCADE_REF          12
11407 #define SETUP_TOKEN_EDITOR_CASCADE_USER         13
11408 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      14
11409
11410 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         15
11411
11412 /* shortcut setup */
11413 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
11414 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
11415 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
11416 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
11417 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
11418 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
11419 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
11420 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
11421 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT         8
11422 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA         9
11423 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP          10
11424 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE         11
11425 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD        12
11426 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY          13
11427 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE       14
11428 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS        15
11429 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC        16
11430 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT          17
11431 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT         18
11432 #define SETUP_TOKEN_SHORTCUT_SNAP_UP            19
11433 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN          20
11434
11435 #define NUM_SHORTCUT_SETUP_TOKENS               21
11436
11437 /* player setup */
11438 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
11439 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
11440 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
11441 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
11442 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
11443 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
11444 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
11445 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
11446 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
11447 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
11448 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
11449 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
11450 #define SETUP_TOKEN_PLAYER_KEY_UP               12
11451 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
11452 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
11453 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
11454
11455 #define NUM_PLAYER_SETUP_TOKENS                 16
11456
11457 /* system setup */
11458 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER      0
11459 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      1
11460 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  2
11461
11462 #define NUM_SYSTEM_SETUP_TOKENS                 3
11463
11464 /* options setup */
11465 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
11466
11467 #define NUM_OPTIONS_SETUP_TOKENS                1
11468
11469
11470 static struct SetupInfo si;
11471 static struct SetupEditorInfo sei;
11472 static struct SetupEditorCascadeInfo seci;
11473 static struct SetupShortcutInfo ssi;
11474 static struct SetupInputInfo sii;
11475 static struct SetupSystemInfo syi;
11476 static struct OptionInfo soi;
11477
11478 static struct TokenInfo global_setup_tokens[] =
11479 {
11480   { TYPE_STRING, &si.player_name,             "player_name"             },
11481   { TYPE_SWITCH, &si.sound,                   "sound"                   },
11482   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
11483   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
11484   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
11485   { TYPE_SWITCH, &si.toons,                   "toons"                   },
11486   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
11487   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
11488   { TYPE_SWITCH, &si.soft_scrolling,          "soft_scrolling"          },
11489   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
11490   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
11491   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
11492   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
11493   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
11494   { TYPE_SWITCH, &si.handicap,                "handicap"                },
11495   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
11496   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
11497   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
11498   { TYPE_STRING, &si.fullscreen_mode,         "fullscreen_mode"         },
11499   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
11500   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
11501   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
11502   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
11503   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
11504   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
11505   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
11506   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
11507   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
11508   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
11509   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
11510   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
11511   { TYPE_STRING, &si.music_set,               "music_set"               },
11512   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
11513   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
11514   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
11515   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
11516   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
11517   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
11518   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
11519   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
11520   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
11521 };
11522
11523 static boolean not_used = FALSE;
11524 static struct TokenInfo editor_setup_tokens[] =
11525 {
11526 #if 1
11527   { TYPE_SWITCH, &not_used,             "editor.el_boulderdash"         },
11528   { TYPE_SWITCH, &not_used,             "editor.el_emerald_mine"        },
11529   { TYPE_SWITCH, &not_used,             "editor.el_emerald_mine_club"   },
11530   { TYPE_SWITCH, &not_used,             "editor.el_more"                },
11531   { TYPE_SWITCH, &not_used,             "editor.el_sokoban"             },
11532   { TYPE_SWITCH, &not_used,             "editor.el_supaplex"            },
11533   { TYPE_SWITCH, &not_used,             "editor.el_diamond_caves"       },
11534   { TYPE_SWITCH, &not_used,             "editor.el_dx_boulderdash"      },
11535 #else
11536   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
11537   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
11538   { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
11539   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
11540   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
11541   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
11542   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
11543   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
11544 #endif
11545   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
11546   { TYPE_SWITCH, &sei.el_steel_chars,   "editor.el_steel_chars"         },
11547   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
11548 #if 1
11549   { TYPE_SWITCH, &not_used,             "editor.el_headlines"           },
11550 #else
11551   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
11552 #endif
11553   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
11554   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
11555   { TYPE_SWITCH, &sei.el_by_game,       "editor.el_by_game"             },
11556   { TYPE_SWITCH, &sei.el_by_type,       "editor.el_by_type"             },
11557   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
11558 };
11559
11560 static struct TokenInfo editor_cascade_setup_tokens[] =
11561 {
11562   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
11563   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
11564   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
11565   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
11566   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
11567   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
11568   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
11569   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
11570   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
11571   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
11572   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
11573   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
11574   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
11575   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
11576   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
11577 };
11578
11579 static struct TokenInfo shortcut_setup_tokens[] =
11580 {
11581   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
11582   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
11583   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
11584   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
11585   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
11586   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
11587   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
11588   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
11589   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
11590   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
11591   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
11592   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
11593   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
11594   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
11595   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
11596   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
11597   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
11598   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
11599   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
11600   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
11601   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
11602 };
11603
11604 static struct TokenInfo player_setup_tokens[] =
11605 {
11606   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
11607   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
11608   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
11609   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
11610   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
11611   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
11612   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
11613   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
11614   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
11615   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
11616   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
11617   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
11618   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
11619   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
11620   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
11621   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
11622 };
11623
11624 static struct TokenInfo system_setup_tokens[] =
11625 {
11626   { TYPE_STRING,  &syi.sdl_videodriver, "system.sdl_videodriver"        },
11627   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
11628   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
11629 };
11630
11631 static struct TokenInfo options_setup_tokens[] =
11632 {
11633   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
11634 };
11635
11636 static char *get_corrected_login_name(char *login_name)
11637 {
11638   /* needed because player name must be a fixed length string */
11639   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
11640
11641   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
11642   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
11643
11644   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
11645     if (strchr(login_name_new, ' '))
11646       *strchr(login_name_new, ' ') = '\0';
11647
11648   return login_name_new;
11649 }
11650
11651 static void setSetupInfoToDefaults(struct SetupInfo *si)
11652 {
11653   int i;
11654
11655   si->player_name = get_corrected_login_name(getLoginName());
11656
11657   si->sound = TRUE;
11658   si->sound_loops = TRUE;
11659   si->sound_music = TRUE;
11660   si->sound_simple = TRUE;
11661   si->toons = TRUE;
11662   si->scroll_delay = TRUE;
11663   si->scroll_delay_value = STD_SCROLL_DELAY;
11664   si->soft_scrolling = TRUE;
11665   si->fade_screens = TRUE;
11666   si->autorecord = TRUE;
11667   si->show_titlescreen = TRUE;
11668   si->quick_doors = FALSE;
11669   si->team_mode = FALSE;
11670   si->handicap = TRUE;
11671   si->skip_levels = TRUE;
11672   si->time_limit = TRUE;
11673   si->fullscreen = FALSE;
11674   si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
11675   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11676   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11677   si->ask_on_escape = TRUE;
11678   si->ask_on_escape_editor = TRUE;
11679   si->quick_switch = FALSE;
11680   si->input_on_focus = FALSE;
11681   si->prefer_aga_graphics = TRUE;
11682   si->game_frame_delay = GAME_FRAME_DELAY;
11683   si->sp_show_border_elements = FALSE;
11684   si->small_game_graphics = FALSE;
11685
11686   si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
11687   si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
11688   si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
11689   si->override_level_graphics = FALSE;
11690   si->override_level_sounds = FALSE;
11691   si->override_level_music = FALSE;
11692
11693   si->volume_simple = 100;              /* percent */
11694   si->volume_loops = 100;               /* percent */
11695   si->volume_music = 100;               /* percent */
11696
11697   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11698   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
11699   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
11700
11701   si->editor.el_boulderdash             = TRUE;
11702   si->editor.el_emerald_mine            = TRUE;
11703   si->editor.el_emerald_mine_club       = TRUE;
11704   si->editor.el_more                    = TRUE;
11705   si->editor.el_sokoban                 = TRUE;
11706   si->editor.el_supaplex                = TRUE;
11707   si->editor.el_diamond_caves           = TRUE;
11708   si->editor.el_dx_boulderdash          = TRUE;
11709   si->editor.el_chars                   = TRUE;
11710   si->editor.el_steel_chars             = TRUE;
11711   si->editor.el_custom                  = TRUE;
11712
11713   si->editor.el_headlines = TRUE;
11714   si->editor.el_user_defined = FALSE;
11715   si->editor.el_dynamic = TRUE;
11716
11717   si->editor.show_element_token = FALSE;
11718
11719   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11720   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11721   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11722
11723   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11724   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11725   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11726   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11727   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11728
11729   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11730   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11731   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11732   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11733   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11734   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11735
11736   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11737   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11738   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11739
11740   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11741   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11742   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11743   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11744
11745   for (i = 0; i < MAX_PLAYERS; i++)
11746   {
11747     si->input[i].use_joystick = FALSE;
11748     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
11749     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11750     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11751     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11752     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11753     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11754     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11755     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11756     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11757     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11758     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11759     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11760     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11761     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11762     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11763   }
11764
11765   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11766   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11767   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11768
11769   si->options.verbose = FALSE;
11770
11771 #if defined(CREATE_SPECIAL_EDITION_RND_JUE)
11772   si->toons = FALSE;
11773   si->handicap = FALSE;
11774   si->fullscreen = TRUE;
11775   si->override_level_graphics = AUTO;
11776   si->override_level_sounds = AUTO;
11777   si->override_level_music = AUTO;
11778 #endif
11779
11780 #if defined(PLATFORM_ANDROID)
11781   si->fullscreen = TRUE;
11782 #endif
11783 }
11784
11785 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11786 {
11787   si->editor_cascade.el_bd              = TRUE;
11788   si->editor_cascade.el_em              = TRUE;
11789   si->editor_cascade.el_emc             = TRUE;
11790   si->editor_cascade.el_rnd             = TRUE;
11791   si->editor_cascade.el_sb              = TRUE;
11792   si->editor_cascade.el_sp              = TRUE;
11793   si->editor_cascade.el_dc              = TRUE;
11794   si->editor_cascade.el_dx              = TRUE;
11795
11796   si->editor_cascade.el_chars           = FALSE;
11797   si->editor_cascade.el_steel_chars     = FALSE;
11798   si->editor_cascade.el_ce              = FALSE;
11799   si->editor_cascade.el_ge              = FALSE;
11800   si->editor_cascade.el_ref             = FALSE;
11801   si->editor_cascade.el_user            = FALSE;
11802   si->editor_cascade.el_dynamic         = FALSE;
11803 }
11804
11805 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
11806 {
11807   int i, pnr;
11808
11809   if (!setup_file_hash)
11810     return;
11811
11812   /* global setup */
11813   si = setup;
11814   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
11815     setSetupInfo(global_setup_tokens, i,
11816                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
11817   setup = si;
11818
11819   /* editor setup */
11820   sei = setup.editor;
11821   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
11822     setSetupInfo(editor_setup_tokens, i,
11823                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
11824   setup.editor = sei;
11825
11826   /* shortcut setup */
11827   ssi = setup.shortcut;
11828   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
11829     setSetupInfo(shortcut_setup_tokens, i,
11830                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
11831   setup.shortcut = ssi;
11832
11833   /* player setup */
11834   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11835   {
11836     char prefix[30];
11837
11838     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11839
11840     sii = setup.input[pnr];
11841     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
11842     {
11843       char full_token[100];
11844
11845       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11846       setSetupInfo(player_setup_tokens, i,
11847                    getHashEntry(setup_file_hash, full_token));
11848     }
11849     setup.input[pnr] = sii;
11850   }
11851
11852   /* system setup */
11853   syi = setup.system;
11854   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
11855     setSetupInfo(system_setup_tokens, i,
11856                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
11857   setup.system = syi;
11858
11859   /* options setup */
11860   soi = setup.options;
11861   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
11862     setSetupInfo(options_setup_tokens, i,
11863                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
11864   setup.options = soi;
11865 }
11866
11867 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11868 {
11869   int i;
11870
11871   if (!setup_file_hash)
11872     return;
11873
11874   /* editor cascade setup */
11875   seci = setup.editor_cascade;
11876   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
11877     setSetupInfo(editor_cascade_setup_tokens, i,
11878                  getHashEntry(setup_file_hash,
11879                               editor_cascade_setup_tokens[i].text));
11880   setup.editor_cascade = seci;
11881 }
11882
11883 void LoadSetup()
11884 {
11885   char *filename = getSetupFilename();
11886   SetupFileHash *setup_file_hash = NULL;
11887
11888   /* always start with reliable default values */
11889   setSetupInfoToDefaults(&setup);
11890
11891   setup_file_hash = loadSetupFileHash(filename);
11892
11893   if (setup_file_hash)
11894   {
11895     char *player_name_new;
11896
11897     checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
11898     decodeSetupFileHash(setup_file_hash);
11899
11900     freeSetupFileHash(setup_file_hash);
11901
11902     /* needed to work around problems with fixed length strings */
11903     player_name_new = get_corrected_login_name(setup.player_name);
11904     free(setup.player_name);
11905     setup.player_name = player_name_new;
11906
11907     /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
11908     if (setup.scroll_delay == FALSE)
11909     {
11910       setup.scroll_delay_value = MIN_SCROLL_DELAY;
11911       setup.scroll_delay = TRUE;                        /* now always "on" */
11912     }
11913
11914     /* make sure that scroll delay value stays inside valid range */
11915     setup.scroll_delay_value =
11916       MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11917   }
11918   else
11919     Error(ERR_WARN, "using default setup values");
11920 }
11921
11922 void LoadSetup_EditorCascade()
11923 {
11924   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11925   SetupFileHash *setup_file_hash = NULL;
11926
11927   /* always start with reliable default values */
11928   setSetupInfoToDefaults_EditorCascade(&setup);
11929
11930   setup_file_hash = loadSetupFileHash(filename);
11931
11932   if (setup_file_hash)
11933   {
11934     checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
11935     decodeSetupFileHash_EditorCascade(setup_file_hash);
11936
11937     freeSetupFileHash(setup_file_hash);
11938   }
11939
11940   free(filename);
11941 }
11942
11943 void SaveSetup()
11944 {
11945   char *filename = getSetupFilename();
11946   FILE *file;
11947   int i, pnr;
11948
11949   InitUserDataDirectory();
11950
11951   if (!(file = fopen(filename, MODE_WRITE)))
11952   {
11953     Error(ERR_WARN, "cannot write setup file '%s'", filename);
11954     return;
11955   }
11956
11957   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
11958                                                getCookie("SETUP")));
11959   fprintf(file, "\n");
11960
11961   /* global setup */
11962   si = setup;
11963   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
11964   {
11965     /* just to make things nicer :) */
11966     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
11967         i == SETUP_TOKEN_GRAPHICS_SET ||
11968         i == SETUP_TOKEN_VOLUME_SIMPLE ||
11969         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
11970       fprintf(file, "\n");
11971
11972     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11973   }
11974
11975   /* editor setup */
11976   sei = setup.editor;
11977   fprintf(file, "\n");
11978   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
11979     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11980
11981   /* shortcut setup */
11982   ssi = setup.shortcut;
11983   fprintf(file, "\n");
11984   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
11985     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11986
11987   /* player setup */
11988   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11989   {
11990     char prefix[30];
11991
11992     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11993     fprintf(file, "\n");
11994
11995     sii = setup.input[pnr];
11996     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
11997       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11998   }
11999
12000   /* system setup */
12001   syi = setup.system;
12002   fprintf(file, "\n");
12003   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
12004     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12005
12006   /* options setup */
12007   soi = setup.options;
12008   fprintf(file, "\n");
12009   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
12010     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12011
12012   fclose(file);
12013
12014   SetFilePermissions(filename, PERMS_PRIVATE);
12015 }
12016
12017 void SaveSetup_EditorCascade()
12018 {
12019   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12020   FILE *file;
12021   int i;
12022
12023   InitUserDataDirectory();
12024
12025   if (!(file = fopen(filename, MODE_WRITE)))
12026   {
12027     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
12028     free(filename);
12029     return;
12030   }
12031
12032   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
12033                                                getCookie("SETUP")));
12034   fprintf(file, "\n");
12035
12036   seci = setup.editor_cascade;
12037   fprintf(file, "\n");
12038   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
12039     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12040
12041   fclose(file);
12042
12043   SetFilePermissions(filename, PERMS_PRIVATE);
12044
12045   free(filename);
12046 }
12047
12048 void LoadCustomElementDescriptions()
12049 {
12050   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12051   SetupFileHash *setup_file_hash;
12052   int i;
12053
12054   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12055   {
12056     if (element_info[i].custom_description != NULL)
12057     {
12058       free(element_info[i].custom_description);
12059       element_info[i].custom_description = NULL;
12060     }
12061   }
12062
12063   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12064     return;
12065
12066   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12067   {
12068     char *token = getStringCat2(element_info[i].token_name, ".name");
12069     char *value = getHashEntry(setup_file_hash, token);
12070
12071     if (value != NULL)
12072       element_info[i].custom_description = getStringCopy(value);
12073
12074     free(token);
12075   }
12076
12077   freeSetupFileHash(setup_file_hash);
12078 }
12079
12080 static int getElementFromToken(char *token)
12081 {
12082 #if 1
12083   char *value = getHashEntry(element_token_hash, token);
12084
12085   if (value != NULL)
12086     return atoi(value);
12087 #else
12088   int i;
12089
12090   /* !!! OPTIMIZE THIS BY USING HASH !!! */
12091   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
12092     if (strEqual(token, element_info[i].token_name))
12093       return i;
12094 #endif
12095
12096   Error(ERR_WARN, "unknown element token '%s'", token);
12097
12098   return EL_UNDEFINED;
12099 }
12100
12101 static int get_token_parameter_value(char *token, char *value_raw)
12102 {
12103   char *suffix;
12104
12105   if (token == NULL || value_raw == NULL)
12106     return ARG_UNDEFINED_VALUE;
12107
12108   suffix = strrchr(token, '.');
12109   if (suffix == NULL)
12110     suffix = token;
12111
12112 #if 1
12113   if (strEqual(suffix, ".element"))
12114     return getElementFromToken(value_raw);
12115 #endif
12116
12117 #if 0
12118   if (strncmp(suffix, ".font", 5) == 0)
12119   {
12120     int i;
12121
12122     /* !!! OPTIMIZE THIS BY USING HASH !!! */
12123     for (i = 0; i < NUM_FONTS; i++)
12124       if (strEqual(value_raw, font_info[i].token_name))
12125         return i;
12126
12127     /* if font not found, use reliable default value */
12128     return FONT_INITIAL_1;
12129   }
12130 #endif
12131
12132   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
12133   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12134 }
12135
12136 void InitMenuDesignSettings_Static()
12137 {
12138 #if 0
12139   static SetupFileHash *image_config_hash = NULL;
12140 #endif
12141   int i;
12142
12143 #if 0
12144   if (image_config_hash == NULL)
12145   {
12146     image_config_hash = newSetupFileHash();
12147
12148     for (i = 0; image_config[i].token != NULL; i++)
12149       setHashEntry(image_config_hash,
12150                    image_config[i].token,
12151                    image_config[i].value);
12152   }
12153 #endif
12154
12155 #if 1
12156   /* always start with reliable default values from static default config */
12157   for (i = 0; image_config_vars[i].token != NULL; i++)
12158   {
12159     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
12160
12161     if (value != NULL)
12162       *image_config_vars[i].value =
12163         get_token_parameter_value(image_config_vars[i].token, value);
12164   }
12165
12166 #else
12167
12168   int j;
12169
12170   /* always start with reliable default values from static default config */
12171   for (i = 0; image_config_vars[i].token != NULL; i++)
12172     for (j = 0; image_config[j].token != NULL; j++)
12173       if (strEqual(image_config_vars[i].token, image_config[j].token))
12174         *image_config_vars[i].value =
12175           get_token_parameter_value(image_config_vars[i].token,
12176                                     image_config[j].value);
12177 #endif
12178 }
12179
12180 static void InitMenuDesignSettings_SpecialPreProcessing()
12181 {
12182   int i;
12183
12184   /* the following initializes hierarchical values from static configuration */
12185
12186   /* special case: initialize "ARG_DEFAULT" values in static default config */
12187   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
12188   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12189   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12190   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12191   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
12192   titlemessage_default.fade_mode  = title_default.fade_mode;
12193   titlemessage_default.fade_delay = title_default.fade_delay;
12194   titlemessage_default.post_delay = title_default.post_delay;
12195   titlemessage_default.auto_delay = title_default.auto_delay;
12196
12197   /* special case: initialize "ARG_DEFAULT" values in static default config */
12198   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
12199   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12200   {
12201     titlemessage_initial[i] = titlemessage_initial_default;
12202     titlemessage[i] = titlemessage_default;
12203   }
12204
12205   /* special case: initialize "ARG_DEFAULT" values in static default config */
12206   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
12207   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12208   {
12209     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12210     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12211   }
12212
12213   /* special case: initialize "ARG_DEFAULT" values in static default config */
12214   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
12215   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12216   {
12217     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12218     viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12219     if (i != GFX_SPECIAL_ARG_EDITOR)    /* editor value already initialized */
12220       viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12221   }
12222 }
12223
12224 static void InitMenuDesignSettings_SpecialPostProcessing()
12225 {
12226   /* special case: initialize later added SETUP list size from LEVELS value */
12227   if (menu.list_size[GAME_MODE_SETUP] == -1)
12228     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12229 }
12230
12231 static void LoadMenuDesignSettingsFromFilename(char *filename)
12232 {
12233   static struct TitleMessageInfo tmi;
12234   static struct TokenInfo titlemessage_tokens[] =
12235   {
12236     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
12237     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
12238     { TYPE_INTEGER,     &tmi.width,             ".width"                },
12239     { TYPE_INTEGER,     &tmi.height,            ".height"               },
12240     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
12241     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
12242     { TYPE_INTEGER,     &tmi.align,             ".align"                },
12243     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
12244     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
12245     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
12246     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
12247     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
12248     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
12249     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
12250     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
12251     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
12252     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
12253
12254     { -1,               NULL,                   NULL                    }
12255   };
12256   static struct
12257   {
12258     struct TitleMessageInfo *array;
12259     char *text;
12260   }
12261   titlemessage_arrays[] =
12262   {
12263     { titlemessage_initial,             "[titlemessage_initial]"        },
12264     { titlemessage,                     "[titlemessage]"                },
12265
12266     { NULL,                             NULL                            }
12267   };
12268   SetupFileHash *setup_file_hash;
12269   int i, j, k;
12270
12271 #if 0
12272   printf("LoadMenuDesignSettings from file '%s' ...\n", filename);
12273 #endif
12274
12275   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12276     return;
12277
12278   /* the following initializes hierarchical values from dynamic configuration */
12279
12280   /* special case: initialize with default values that may be overwritten */
12281   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
12282   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12283   {
12284     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
12285     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
12286     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
12287
12288     if (value_1 != NULL)
12289       menu.draw_xoffset[i] = get_integer_from_string(value_1);
12290     if (value_2 != NULL)
12291       menu.draw_yoffset[i] = get_integer_from_string(value_2);
12292     if (value_3 != NULL)
12293       menu.list_size[i] = get_integer_from_string(value_3);
12294   }
12295
12296   /* special case: initialize with default values that may be overwritten */
12297   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
12298   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12299   {
12300     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
12301     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
12302
12303     if (value_1 != NULL)
12304       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
12305     if (value_2 != NULL)
12306       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
12307   }
12308
12309   /* special case: initialize with default values that may be overwritten */
12310   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
12311   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12312   {
12313     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
12314     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
12315
12316     if (value_1 != NULL)
12317       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
12318     if (value_2 != NULL)
12319       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
12320   }
12321
12322   /* special case: initialize with default values that may be overwritten */
12323   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
12324   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12325   {
12326     char *token_1 = "menu.enter_screen.fade_mode";
12327     char *token_2 = "menu.enter_screen.fade_delay";
12328     char *token_3 = "menu.enter_screen.post_delay";
12329     char *token_4 = "menu.leave_screen.fade_mode";
12330     char *token_5 = "menu.leave_screen.fade_delay";
12331     char *token_6 = "menu.leave_screen.post_delay";
12332     char *value_1 = getHashEntry(setup_file_hash, token_1);
12333     char *value_2 = getHashEntry(setup_file_hash, token_2);
12334     char *value_3 = getHashEntry(setup_file_hash, token_3);
12335     char *value_4 = getHashEntry(setup_file_hash, token_4);
12336     char *value_5 = getHashEntry(setup_file_hash, token_5);
12337     char *value_6 = getHashEntry(setup_file_hash, token_6);
12338
12339     if (value_1 != NULL)
12340       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
12341                                                                  value_1);
12342     if (value_2 != NULL)
12343       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
12344                                                                   value_2);
12345     if (value_3 != NULL)
12346       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
12347                                                                   value_3);
12348     if (value_4 != NULL)
12349       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
12350                                                                  value_4);
12351     if (value_5 != NULL)
12352       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
12353                                                                   value_5);
12354     if (value_6 != NULL)
12355       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
12356                                                                   value_6);
12357   }
12358
12359   /* special case: initialize with default values that may be overwritten */
12360   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
12361   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12362   {
12363     char *token_01 = "viewport.playfield.x";
12364     char *token_02 = "viewport.playfield.y";
12365     char *token_03 = "viewport.playfield.width";
12366     char *token_04 = "viewport.playfield.height";
12367     char *token_05 = "viewport.playfield.border_size";
12368     char *token_06 = "viewport.door_1.x";
12369     char *token_07 = "viewport.door_1.y";
12370     char *token_08 = "viewport.door_1.width";
12371     char *token_09 = "viewport.door_1.height";
12372     char *token_10 = "viewport.door_1.border_size";
12373     char *token_11 = "viewport.door_2.x";
12374     char *token_12 = "viewport.door_2.y";
12375     char *token_13 = "viewport.door_2.width";
12376     char *token_14 = "viewport.door_2.height";
12377     char *token_15 = "viewport.door_2.border_size";
12378     char *value_01 = getHashEntry(setup_file_hash, token_01);
12379     char *value_02 = getHashEntry(setup_file_hash, token_02);
12380     char *value_03 = getHashEntry(setup_file_hash, token_03);
12381     char *value_04 = getHashEntry(setup_file_hash, token_04);
12382     char *value_05 = getHashEntry(setup_file_hash, token_05);
12383     char *value_06 = getHashEntry(setup_file_hash, token_06);
12384     char *value_07 = getHashEntry(setup_file_hash, token_07);
12385     char *value_08 = getHashEntry(setup_file_hash, token_08);
12386     char *value_09 = getHashEntry(setup_file_hash, token_09);
12387     char *value_10 = getHashEntry(setup_file_hash, token_10);
12388     char *value_11 = getHashEntry(setup_file_hash, token_11);
12389     char *value_12 = getHashEntry(setup_file_hash, token_12);
12390     char *value_13 = getHashEntry(setup_file_hash, token_13);
12391     char *value_14 = getHashEntry(setup_file_hash, token_14);
12392     char *value_15 = getHashEntry(setup_file_hash, token_15);
12393
12394     if (value_01 != NULL)
12395       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
12396     if (value_02 != NULL)
12397       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
12398     if (value_03 != NULL)
12399       viewport.playfield[i].width = get_token_parameter_value(token_03,
12400                                                               value_03);
12401     if (value_04 != NULL)
12402       viewport.playfield[i].height = get_token_parameter_value(token_04,
12403                                                                value_04);
12404     if (value_05 != NULL)
12405       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
12406                                                                     value_05);
12407     if (value_06 != NULL)
12408       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
12409     if (value_07 != NULL)
12410       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
12411     if (value_08 != NULL)
12412       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
12413     if (value_09 != NULL)
12414       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
12415     if (value_10 != NULL)
12416       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
12417                                                                  value_10);
12418     if (value_11 != NULL)
12419       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
12420     if (value_12 != NULL)
12421       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
12422     if (value_13 != NULL)
12423       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
12424     if (value_14 != NULL)
12425       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
12426     if (value_15 != NULL)
12427       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
12428                                                                  value_15);
12429   }
12430
12431   /* special case: initialize with default values that may be overwritten */
12432   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
12433   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12434   {
12435     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12436     char *base_token = titlemessage_arrays[i].text;
12437
12438     for (j = 0; titlemessage_tokens[j].type != -1; j++)
12439     {
12440       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12441       char *value = getHashEntry(setup_file_hash, token);
12442
12443       if (value != NULL)
12444       {
12445         int parameter_value = get_token_parameter_value(token, value);
12446
12447         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12448         {
12449           tmi = array[k];
12450
12451           if (titlemessage_tokens[j].type == TYPE_INTEGER)
12452             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12453           else
12454             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
12455
12456           array[k] = tmi;
12457         }
12458       }
12459
12460       free(token);
12461     }
12462   }
12463
12464   /* read (and overwrite with) values that may be specified in config file */
12465   for (i = 0; image_config_vars[i].token != NULL; i++)
12466   {
12467     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12468
12469     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
12470     if (value != NULL && !strEqual(value, ARG_DEFAULT))
12471       *image_config_vars[i].value =
12472         get_token_parameter_value(image_config_vars[i].token, value);
12473   }
12474
12475   freeSetupFileHash(setup_file_hash);
12476 }
12477
12478 void LoadMenuDesignSettings()
12479 {
12480   char *filename_base = UNDEFINED_FILENAME, *filename_local;
12481
12482   InitMenuDesignSettings_Static();
12483   InitMenuDesignSettings_SpecialPreProcessing();
12484
12485 #if 1
12486   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12487 #else
12488   if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
12489 #endif
12490   {
12491     /* first look for special settings configured in level series config */
12492     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12493
12494     if (fileExists(filename_base))
12495       LoadMenuDesignSettingsFromFilename(filename_base);
12496   }
12497
12498   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12499
12500   if (filename_local != NULL && !strEqual(filename_base, filename_local))
12501     LoadMenuDesignSettingsFromFilename(filename_local);
12502
12503   InitMenuDesignSettings_SpecialPostProcessing();
12504 }
12505
12506 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12507 {
12508   char *filename = getEditorSetupFilename();
12509   SetupFileList *setup_file_list, *list;
12510   SetupFileHash *element_hash;
12511   int num_unknown_tokens = 0;
12512   int i;
12513
12514   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12515     return;
12516
12517   element_hash = newSetupFileHash();
12518
12519   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12520     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12521
12522   /* determined size may be larger than needed (due to unknown elements) */
12523   *num_elements = 0;
12524   for (list = setup_file_list; list != NULL; list = list->next)
12525     (*num_elements)++;
12526
12527   /* add space for up to 3 more elements for padding that may be needed */
12528   *num_elements += 3;
12529
12530   /* free memory for old list of elements, if needed */
12531   checked_free(*elements);
12532
12533   /* allocate memory for new list of elements */
12534   *elements = checked_malloc(*num_elements * sizeof(int));
12535
12536   *num_elements = 0;
12537   for (list = setup_file_list; list != NULL; list = list->next)
12538   {
12539     char *value = getHashEntry(element_hash, list->token);
12540
12541     if (value == NULL)          /* try to find obsolete token mapping */
12542     {
12543       char *mapped_token = get_mapped_token(list->token);
12544
12545       if (mapped_token != NULL)
12546       {
12547         value = getHashEntry(element_hash, mapped_token);
12548
12549         free(mapped_token);
12550       }
12551     }
12552
12553     if (value != NULL)
12554     {
12555       (*elements)[(*num_elements)++] = atoi(value);
12556     }
12557     else
12558     {
12559       if (num_unknown_tokens == 0)
12560       {
12561         Error(ERR_INFO_LINE, "-");
12562         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
12563         Error(ERR_INFO, "- config file: '%s'", filename);
12564
12565         num_unknown_tokens++;
12566       }
12567
12568       Error(ERR_INFO, "- token: '%s'", list->token);
12569     }
12570   }
12571
12572   if (num_unknown_tokens > 0)
12573     Error(ERR_INFO_LINE, "-");
12574
12575   while (*num_elements % 4)     /* pad with empty elements, if needed */
12576     (*elements)[(*num_elements)++] = EL_EMPTY;
12577
12578   freeSetupFileList(setup_file_list);
12579   freeSetupFileHash(element_hash);
12580
12581 #if 0
12582   for (i = 0; i < *num_elements; i++)
12583     printf("editor: element '%s' [%d]\n",
12584            element_info[(*elements)[i]].token_name, (*elements)[i]);
12585 #endif
12586 }
12587
12588 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12589                                                      boolean is_sound)
12590 {
12591   SetupFileHash *setup_file_hash = NULL;
12592   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12593   char *filename_music, *filename_prefix, *filename_info;
12594   struct
12595   {
12596     char *token;
12597     char **value_ptr;
12598   }
12599   token_to_value_ptr[] =
12600   {
12601     { "title_header",   &tmp_music_file_info.title_header       },
12602     { "artist_header",  &tmp_music_file_info.artist_header      },
12603     { "album_header",   &tmp_music_file_info.album_header       },
12604     { "year_header",    &tmp_music_file_info.year_header        },
12605
12606     { "title",          &tmp_music_file_info.title              },
12607     { "artist",         &tmp_music_file_info.artist             },
12608     { "album",          &tmp_music_file_info.album              },
12609     { "year",           &tmp_music_file_info.year               },
12610
12611     { NULL,             NULL                                    },
12612   };
12613   int i;
12614
12615   filename_music = (is_sound ? getCustomSoundFilename(basename) :
12616                     getCustomMusicFilename(basename));
12617
12618   if (filename_music == NULL)
12619     return NULL;
12620
12621   /* ---------- try to replace file extension ---------- */
12622
12623   filename_prefix = getStringCopy(filename_music);
12624   if (strrchr(filename_prefix, '.') != NULL)
12625     *strrchr(filename_prefix, '.') = '\0';
12626   filename_info = getStringCat2(filename_prefix, ".txt");
12627
12628 #if 0
12629   printf("trying to load file '%s'...\n", filename_info);
12630 #endif
12631
12632   if (fileExists(filename_info))
12633     setup_file_hash = loadSetupFileHash(filename_info);
12634
12635   free(filename_prefix);
12636   free(filename_info);
12637
12638   if (setup_file_hash == NULL)
12639   {
12640     /* ---------- try to add file extension ---------- */
12641
12642     filename_prefix = getStringCopy(filename_music);
12643     filename_info = getStringCat2(filename_prefix, ".txt");
12644
12645 #if 0
12646     printf("trying to load file '%s'...\n", filename_info);
12647 #endif
12648
12649     if (fileExists(filename_info))
12650       setup_file_hash = loadSetupFileHash(filename_info);
12651
12652     free(filename_prefix);
12653     free(filename_info);
12654   }
12655
12656   if (setup_file_hash == NULL)
12657     return NULL;
12658
12659   /* ---------- music file info found ---------- */
12660
12661   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12662
12663   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12664   {
12665     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12666
12667     *token_to_value_ptr[i].value_ptr =
12668       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12669   }
12670
12671   tmp_music_file_info.basename = getStringCopy(basename);
12672   tmp_music_file_info.music = music;
12673   tmp_music_file_info.is_sound = is_sound;
12674
12675   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12676   *new_music_file_info = tmp_music_file_info;
12677
12678   return new_music_file_info;
12679 }
12680
12681 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12682 {
12683   return get_music_file_info_ext(basename, music, FALSE);
12684 }
12685
12686 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12687 {
12688   return get_music_file_info_ext(basename, sound, TRUE);
12689 }
12690
12691 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12692                                      char *basename, boolean is_sound)
12693 {
12694   for (; list != NULL; list = list->next)
12695     if (list->is_sound == is_sound && strEqual(list->basename, basename))
12696       return TRUE;
12697
12698   return FALSE;
12699 }
12700
12701 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12702 {
12703   return music_info_listed_ext(list, basename, FALSE);
12704 }
12705
12706 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12707 {
12708   return music_info_listed_ext(list, basename, TRUE);
12709 }
12710
12711 #if 1
12712
12713 void LoadMusicInfo()
12714 {
12715   char *music_directory = getCustomMusicDirectory();
12716   int num_music = getMusicListSize();
12717   int num_music_noconf = 0;
12718   int num_sounds = getSoundListSize();
12719   Directory *dir;
12720   DirectoryEntry *dir_entry;
12721   struct FileInfo *music, *sound;
12722   struct MusicFileInfo *next, **new;
12723   int i;
12724
12725   while (music_file_info != NULL)
12726   {
12727     next = music_file_info->next;
12728
12729     checked_free(music_file_info->basename);
12730
12731     checked_free(music_file_info->title_header);
12732     checked_free(music_file_info->artist_header);
12733     checked_free(music_file_info->album_header);
12734     checked_free(music_file_info->year_header);
12735
12736     checked_free(music_file_info->title);
12737     checked_free(music_file_info->artist);
12738     checked_free(music_file_info->album);
12739     checked_free(music_file_info->year);
12740
12741     free(music_file_info);
12742
12743     music_file_info = next;
12744   }
12745
12746   new = &music_file_info;
12747
12748   for (i = 0; i < num_music; i++)
12749   {
12750     music = getMusicListEntry(i);
12751
12752     if (music->filename == NULL)
12753       continue;
12754
12755     if (strEqual(music->filename, UNDEFINED_FILENAME))
12756       continue;
12757
12758     /* a configured file may be not recognized as music */
12759     if (!FileIsMusic(music->filename))
12760       continue;
12761
12762 #if 0
12763     printf("::: -> '%s' (configured)\n", music->filename);
12764 #endif
12765
12766     if (!music_info_listed(music_file_info, music->filename))
12767     {
12768       *new = get_music_file_info(music->filename, i);
12769 #if 0
12770       if (*new != NULL)
12771         printf(":1: adding '%s' ['%s'] ...\n", (*new)->title, music->filename);
12772 #endif
12773       if (*new != NULL)
12774         new = &(*new)->next;
12775     }
12776   }
12777
12778   if ((dir = openDirectory(music_directory)) == NULL)
12779   {
12780     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
12781     return;
12782   }
12783
12784   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
12785   {
12786     char *basename = dir_entry->basename;
12787     boolean music_already_used = FALSE;
12788     int i;
12789
12790     /* skip all music files that are configured in music config file */
12791     for (i = 0; i < num_music; i++)
12792     {
12793       music = getMusicListEntry(i);
12794
12795       if (music->filename == NULL)
12796         continue;
12797
12798       if (strEqual(basename, music->filename))
12799       {
12800         music_already_used = TRUE;
12801         break;
12802       }
12803     }
12804
12805     if (music_already_used)
12806       continue;
12807
12808     if (!FileIsMusic(basename))
12809       continue;
12810
12811 #if 0
12812     printf("::: -> '%s' (found in directory)\n", basename);
12813 #endif
12814
12815     if (!music_info_listed(music_file_info, basename))
12816     {
12817       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12818 #if 0
12819       if (*new != NULL)
12820         printf(":2: adding '%s' ['%s'] ...\n", (*new)->title, basename);
12821 #endif
12822       if (*new != NULL)
12823         new = &(*new)->next;
12824     }
12825
12826     num_music_noconf++;
12827   }
12828
12829   closeDirectory(dir);
12830
12831   for (i = 0; i < num_sounds; i++)
12832   {
12833     sound = getSoundListEntry(i);
12834
12835     if (sound->filename == NULL)
12836       continue;
12837
12838     if (strEqual(sound->filename, UNDEFINED_FILENAME))
12839       continue;
12840
12841     /* a configured file may be not recognized as sound */
12842     if (!FileIsSound(sound->filename))
12843       continue;
12844
12845 #if 0
12846     printf("::: -> '%s' (configured)\n", sound->filename);
12847 #endif
12848
12849     if (!sound_info_listed(music_file_info, sound->filename))
12850     {
12851       *new = get_sound_file_info(sound->filename, i);
12852       if (*new != NULL)
12853         new = &(*new)->next;
12854     }
12855   }
12856
12857 #if 0
12858   for (next = music_file_info; next != NULL; next = next->next)
12859     printf("::: title == '%s'\n", next->title);
12860 #endif
12861 }
12862
12863 #else
12864
12865 void LoadMusicInfo()
12866 {
12867   char *music_directory = getCustomMusicDirectory();
12868   int num_music = getMusicListSize();
12869   int num_music_noconf = 0;
12870   int num_sounds = getSoundListSize();
12871   DIR *dir;
12872   struct dirent *dir_entry;
12873   struct FileInfo *music, *sound;
12874   struct MusicFileInfo *next, **new;
12875   int i;
12876
12877   while (music_file_info != NULL)
12878   {
12879     next = music_file_info->next;
12880
12881     checked_free(music_file_info->basename);
12882
12883     checked_free(music_file_info->title_header);
12884     checked_free(music_file_info->artist_header);
12885     checked_free(music_file_info->album_header);
12886     checked_free(music_file_info->year_header);
12887
12888     checked_free(music_file_info->title);
12889     checked_free(music_file_info->artist);
12890     checked_free(music_file_info->album);
12891     checked_free(music_file_info->year);
12892
12893     free(music_file_info);
12894
12895     music_file_info = next;
12896   }
12897
12898   new = &music_file_info;
12899
12900   for (i = 0; i < num_music; i++)
12901   {
12902     music = getMusicListEntry(i);
12903
12904     if (music->filename == NULL)
12905       continue;
12906
12907     if (strEqual(music->filename, UNDEFINED_FILENAME))
12908       continue;
12909
12910     /* a configured file may be not recognized as music */
12911     if (!FileIsMusic(music->filename))
12912       continue;
12913
12914 #if 0
12915     printf("::: -> '%s' (configured)\n", music->filename);
12916 #endif
12917
12918     if (!music_info_listed(music_file_info, music->filename))
12919     {
12920       *new = get_music_file_info(music->filename, i);
12921 #if 0
12922       if (*new != NULL)
12923         printf(":1: adding '%s' ['%s'] ...\n", (*new)->title, music->filename);
12924 #endif
12925       if (*new != NULL)
12926         new = &(*new)->next;
12927     }
12928   }
12929
12930   if ((dir = opendir(music_directory)) == NULL)
12931   {
12932     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
12933     return;
12934   }
12935
12936   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
12937   {
12938     char *basename = dir_entry->d_name;
12939     boolean music_already_used = FALSE;
12940     int i;
12941
12942     /* skip all music files that are configured in music config file */
12943     for (i = 0; i < num_music; i++)
12944     {
12945       music = getMusicListEntry(i);
12946
12947       if (music->filename == NULL)
12948         continue;
12949
12950       if (strEqual(basename, music->filename))
12951       {
12952         music_already_used = TRUE;
12953         break;
12954       }
12955     }
12956
12957     if (music_already_used)
12958       continue;
12959
12960     if (!FileIsMusic(basename))
12961       continue;
12962
12963 #if 0
12964     printf("::: -> '%s' (found in directory)\n", basename);
12965 #endif
12966
12967     if (!music_info_listed(music_file_info, basename))
12968     {
12969       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12970 #if 0
12971       if (*new != NULL)
12972         printf(":2: adding '%s' ['%s'] ...\n", (*new)->title, basename);
12973 #endif
12974       if (*new != NULL)
12975         new = &(*new)->next;
12976     }
12977
12978     num_music_noconf++;
12979   }
12980
12981   closedir(dir);
12982
12983   for (i = 0; i < num_sounds; i++)
12984   {
12985     sound = getSoundListEntry(i);
12986
12987     if (sound->filename == NULL)
12988       continue;
12989
12990     if (strEqual(sound->filename, UNDEFINED_FILENAME))
12991       continue;
12992
12993     /* a configured file may be not recognized as sound */
12994     if (!FileIsSound(sound->filename))
12995       continue;
12996
12997 #if 0
12998     printf("::: -> '%s' (configured)\n", sound->filename);
12999 #endif
13000
13001     if (!sound_info_listed(music_file_info, sound->filename))
13002     {
13003       *new = get_sound_file_info(sound->filename, i);
13004       if (*new != NULL)
13005         new = &(*new)->next;
13006     }
13007   }
13008
13009 #if 0
13010   for (next = music_file_info; next != NULL; next = next->next)
13011     printf("::: title == '%s'\n", next->title);
13012 #endif
13013 }
13014
13015 #endif
13016
13017 void add_helpanim_entry(int element, int action, int direction, int delay,
13018                         int *num_list_entries)
13019 {
13020   struct HelpAnimInfo *new_list_entry;
13021   (*num_list_entries)++;
13022
13023   helpanim_info =
13024     checked_realloc(helpanim_info,
13025                     *num_list_entries * sizeof(struct HelpAnimInfo));
13026   new_list_entry = &helpanim_info[*num_list_entries - 1];
13027
13028   new_list_entry->element = element;
13029   new_list_entry->action = action;
13030   new_list_entry->direction = direction;
13031   new_list_entry->delay = delay;
13032 }
13033
13034 void print_unknown_token(char *filename, char *token, int token_nr)
13035 {
13036   if (token_nr == 0)
13037   {
13038     Error(ERR_INFO_LINE, "-");
13039     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
13040     Error(ERR_INFO, "- config file: '%s'", filename);
13041   }
13042
13043   Error(ERR_INFO, "- token: '%s'", token);
13044 }
13045
13046 void print_unknown_token_end(int token_nr)
13047 {
13048   if (token_nr > 0)
13049     Error(ERR_INFO_LINE, "-");
13050 }
13051
13052 void LoadHelpAnimInfo()
13053 {
13054   char *filename = getHelpAnimFilename();
13055   SetupFileList *setup_file_list = NULL, *list;
13056   SetupFileHash *element_hash, *action_hash, *direction_hash;
13057   int num_list_entries = 0;
13058   int num_unknown_tokens = 0;
13059   int i;
13060
13061   if (fileExists(filename))
13062     setup_file_list = loadSetupFileList(filename);
13063
13064   if (setup_file_list == NULL)
13065   {
13066     /* use reliable default values from static configuration */
13067     SetupFileList *insert_ptr;
13068
13069     insert_ptr = setup_file_list =
13070       newSetupFileList(helpanim_config[0].token,
13071                        helpanim_config[0].value);
13072
13073     for (i = 1; helpanim_config[i].token; i++)
13074       insert_ptr = addListEntry(insert_ptr,
13075                                 helpanim_config[i].token,
13076                                 helpanim_config[i].value);
13077   }
13078
13079   element_hash   = newSetupFileHash();
13080   action_hash    = newSetupFileHash();
13081   direction_hash = newSetupFileHash();
13082
13083   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13084     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13085
13086   for (i = 0; i < NUM_ACTIONS; i++)
13087     setHashEntry(action_hash, element_action_info[i].suffix,
13088                  i_to_a(element_action_info[i].value));
13089
13090   /* do not store direction index (bit) here, but direction value! */
13091   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13092     setHashEntry(direction_hash, element_direction_info[i].suffix,
13093                  i_to_a(1 << element_direction_info[i].value));
13094
13095   for (list = setup_file_list; list != NULL; list = list->next)
13096   {
13097     char *element_token, *action_token, *direction_token;
13098     char *element_value, *action_value, *direction_value;
13099     int delay = atoi(list->value);
13100
13101     if (strEqual(list->token, "end"))
13102     {
13103       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13104
13105       continue;
13106     }
13107
13108     /* first try to break element into element/action/direction parts;
13109        if this does not work, also accept combined "element[.act][.dir]"
13110        elements (like "dynamite.active"), which are unique elements */
13111
13112     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
13113     {
13114       element_value = getHashEntry(element_hash, list->token);
13115       if (element_value != NULL)        /* element found */
13116         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13117                            &num_list_entries);
13118       else
13119       {
13120         /* no further suffixes found -- this is not an element */
13121         print_unknown_token(filename, list->token, num_unknown_tokens++);
13122       }
13123
13124       continue;
13125     }
13126
13127     /* token has format "<prefix>.<something>" */
13128
13129     action_token = strchr(list->token, '.');    /* suffix may be action ... */
13130     direction_token = action_token;             /* ... or direction */
13131
13132     element_token = getStringCopy(list->token);
13133     *strchr(element_token, '.') = '\0';
13134
13135     element_value = getHashEntry(element_hash, element_token);
13136
13137     if (element_value == NULL)          /* this is no element */
13138     {
13139       element_value = getHashEntry(element_hash, list->token);
13140       if (element_value != NULL)        /* combined element found */
13141         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13142                            &num_list_entries);
13143       else
13144         print_unknown_token(filename, list->token, num_unknown_tokens++);
13145
13146       free(element_token);
13147
13148       continue;
13149     }
13150
13151     action_value = getHashEntry(action_hash, action_token);
13152
13153     if (action_value != NULL)           /* action found */
13154     {
13155       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13156                     &num_list_entries);
13157
13158       free(element_token);
13159
13160       continue;
13161     }
13162
13163     direction_value = getHashEntry(direction_hash, direction_token);
13164
13165     if (direction_value != NULL)        /* direction found */
13166     {
13167       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13168                          &num_list_entries);
13169
13170       free(element_token);
13171
13172       continue;
13173     }
13174
13175     if (strchr(action_token + 1, '.') == NULL)
13176     {
13177       /* no further suffixes found -- this is not an action nor direction */
13178
13179       element_value = getHashEntry(element_hash, list->token);
13180       if (element_value != NULL)        /* combined element found */
13181         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13182                            &num_list_entries);
13183       else
13184         print_unknown_token(filename, list->token, num_unknown_tokens++);
13185
13186       free(element_token);
13187
13188       continue;
13189     }
13190
13191     /* token has format "<prefix>.<suffix>.<something>" */
13192
13193     direction_token = strchr(action_token + 1, '.');
13194
13195     action_token = getStringCopy(action_token);
13196     *strchr(action_token + 1, '.') = '\0';
13197
13198     action_value = getHashEntry(action_hash, action_token);
13199
13200     if (action_value == NULL)           /* this is no action */
13201     {
13202       element_value = getHashEntry(element_hash, list->token);
13203       if (element_value != NULL)        /* combined element found */
13204         add_helpanim_entry(atoi(element_value), -1, -1, delay,
13205                            &num_list_entries);
13206       else
13207         print_unknown_token(filename, list->token, num_unknown_tokens++);
13208
13209       free(element_token);
13210       free(action_token);
13211
13212       continue;
13213     }
13214
13215     direction_value = getHashEntry(direction_hash, direction_token);
13216
13217     if (direction_value != NULL)        /* direction found */
13218     {
13219       add_helpanim_entry(atoi(element_value), atoi(action_value),
13220                          atoi(direction_value), delay, &num_list_entries);
13221
13222       free(element_token);
13223       free(action_token);
13224
13225       continue;
13226     }
13227
13228     /* this is no direction */
13229
13230     element_value = getHashEntry(element_hash, list->token);
13231     if (element_value != NULL)          /* combined element found */
13232       add_helpanim_entry(atoi(element_value), -1, -1, delay,
13233                          &num_list_entries);
13234     else
13235       print_unknown_token(filename, list->token, num_unknown_tokens++);
13236
13237     free(element_token);
13238     free(action_token);
13239   }
13240
13241   print_unknown_token_end(num_unknown_tokens);
13242
13243   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13244   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
13245
13246   freeSetupFileList(setup_file_list);
13247   freeSetupFileHash(element_hash);
13248   freeSetupFileHash(action_hash);
13249   freeSetupFileHash(direction_hash);
13250
13251 #if 0
13252   for (i = 0; i < num_list_entries; i++)
13253     printf("::: '%s': %d, %d, %d => %d\n",
13254            EL_NAME(helpanim_info[i].element),
13255            helpanim_info[i].element,
13256            helpanim_info[i].action,
13257            helpanim_info[i].direction,
13258            helpanim_info[i].delay);
13259 #endif
13260 }
13261
13262 void LoadHelpTextInfo()
13263 {
13264   char *filename = getHelpTextFilename();
13265   int i;
13266
13267   if (helptext_info != NULL)
13268   {
13269     freeSetupFileHash(helptext_info);
13270     helptext_info = NULL;
13271   }
13272
13273   if (fileExists(filename))
13274     helptext_info = loadSetupFileHash(filename);
13275
13276   if (helptext_info == NULL)
13277   {
13278     /* use reliable default values from static configuration */
13279     helptext_info = newSetupFileHash();
13280
13281     for (i = 0; helptext_config[i].token; i++)
13282       setHashEntry(helptext_info,
13283                    helptext_config[i].token,
13284                    helptext_config[i].value);
13285   }
13286
13287 #if 0
13288   BEGIN_HASH_ITERATION(helptext_info, itr)
13289   {
13290     printf("::: '%s' => '%s'\n",
13291            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13292   }
13293   END_HASH_ITERATION(hash, itr)
13294 #endif
13295 }
13296
13297
13298 /* ------------------------------------------------------------------------- */
13299 /* convert levels                                                            */
13300 /* ------------------------------------------------------------------------- */
13301
13302 #define MAX_NUM_CONVERT_LEVELS          1000
13303
13304 void ConvertLevels()
13305 {
13306   static LevelDirTree *convert_leveldir = NULL;
13307   static int convert_level_nr = -1;
13308   static int num_levels_handled = 0;
13309   static int num_levels_converted = 0;
13310   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13311   int i;
13312
13313   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13314                                                global.convert_leveldir);
13315
13316   if (convert_leveldir == NULL)
13317     Error(ERR_EXIT, "no such level identifier: '%s'",
13318           global.convert_leveldir);
13319
13320   leveldir_current = convert_leveldir;
13321
13322   if (global.convert_level_nr != -1)
13323   {
13324     convert_leveldir->first_level = global.convert_level_nr;
13325     convert_leveldir->last_level  = global.convert_level_nr;
13326   }
13327
13328   convert_level_nr = convert_leveldir->first_level;
13329
13330   printf_line("=", 79);
13331   printf("Converting levels\n");
13332   printf_line("-", 79);
13333   printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
13334   printf("Level series name:       '%s'\n", convert_leveldir->name);
13335   printf("Level series author:     '%s'\n", convert_leveldir->author);
13336   printf("Number of levels:        %d\n",   convert_leveldir->levels);
13337   printf_line("=", 79);
13338   printf("\n");
13339
13340   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13341     levels_failed[i] = FALSE;
13342
13343   while (convert_level_nr <= convert_leveldir->last_level)
13344   {
13345     char *level_filename;
13346     boolean new_level;
13347
13348     level_nr = convert_level_nr++;
13349
13350     printf("Level %03d: ", level_nr);
13351
13352     LoadLevel(level_nr);
13353     if (level.no_valid_file)
13354     {
13355       printf("(no level)\n");
13356       continue;
13357     }
13358
13359     printf("converting level ... ");
13360
13361     level_filename = getDefaultLevelFilename(level_nr);
13362     new_level = !fileExists(level_filename);
13363
13364     if (new_level)
13365     {
13366       SaveLevel(level_nr);
13367
13368       num_levels_converted++;
13369
13370       printf("converted.\n");
13371     }
13372     else
13373     {
13374       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13375         levels_failed[level_nr] = TRUE;
13376
13377       printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13378     }
13379
13380     num_levels_handled++;
13381   }
13382
13383   printf("\n");
13384   printf_line("=", 79);
13385   printf("Number of levels handled: %d\n", num_levels_handled);
13386   printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13387          (num_levels_handled ?
13388           num_levels_converted * 100 / num_levels_handled : 0));
13389   printf_line("-", 79);
13390   printf("Summary (for automatic parsing by scripts):\n");
13391   printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13392          convert_leveldir->identifier, num_levels_converted,
13393          num_levels_handled,
13394          (num_levels_handled ?
13395           num_levels_converted * 100 / num_levels_handled : 0));
13396
13397   if (num_levels_handled != num_levels_converted)
13398   {
13399     printf(", FAILED:");
13400     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13401       if (levels_failed[i])
13402         printf(" %03d", i);
13403   }
13404
13405   printf("\n");
13406   printf_line("=", 79);
13407
13408   CloseAllAndExit(0);
13409 }
13410
13411
13412 /* ------------------------------------------------------------------------- */
13413 /* create and save images for use in level sketches (raw BMP format)         */
13414 /* ------------------------------------------------------------------------- */
13415
13416 void CreateLevelSketchImages()
13417 {
13418 #if defined(TARGET_SDL)
13419   Bitmap *bitmap1;
13420   Bitmap *bitmap2;
13421   int i;
13422
13423   InitElementPropertiesGfxElement();
13424
13425   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13426   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13427
13428   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13429   {
13430     Bitmap *src_bitmap;
13431     int src_x, src_y;
13432     int element = getMappedElement(i);
13433     int graphic = el2edimg(element);
13434     char basename1[16];
13435     char basename2[16];
13436     char *filename1;
13437     char *filename2;
13438
13439     sprintf(basename1, "%03d.bmp", i);
13440     sprintf(basename2, "%03ds.bmp", i);
13441
13442     filename1 = getPath2(global.create_images_dir, basename1);
13443     filename2 = getPath2(global.create_images_dir, basename2);
13444
13445     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13446     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
13447                0, 0);
13448
13449     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13450       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
13451
13452     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
13453     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
13454
13455     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13456       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
13457
13458     free(filename1);
13459     free(filename2);
13460
13461     if (options.debug)
13462       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13463   }
13464
13465   FreeBitmap(bitmap1);
13466   FreeBitmap(bitmap2);
13467
13468   if (options.debug)
13469     printf("\n");
13470
13471   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
13472
13473   CloseAllAndExit(0);
13474 #endif
13475 }
13476
13477
13478 /* ------------------------------------------------------------------------- */
13479 /* create and save images for custom and group elements (raw BMP format)     */
13480 /* ------------------------------------------------------------------------- */
13481
13482 void CreateCustomElementImages()
13483 {
13484 #if defined(TARGET_SDL)
13485   char *filename = "graphics.classic/RocksCE.bmp";
13486   Bitmap *bitmap;
13487   Bitmap *src_bitmap;
13488   int dummy_graphic = IMG_CUSTOM_99;
13489   int yoffset_ce = 0;
13490   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13491   int src_x, src_y;
13492   int i;
13493
13494   bitmap = CreateBitmap(TILEX * 16 * 2,
13495                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13496                         DEFAULT_DEPTH);
13497
13498   getFixedGraphicSource(dummy_graphic, 0, &src_bitmap, &src_x, &src_y);
13499
13500   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13501   {
13502     int x = i % 16;
13503     int y = i / 16;
13504     int ii = i + 1;
13505     int j;
13506
13507     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13508                TILEX * x, TILEY * y + yoffset_ce);
13509
13510     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13511                TILEX, TILEY,
13512                TILEX * x + TILEX * 16,
13513                TILEY * y + yoffset_ce);
13514
13515     for (j = 2; j >= 0; j--)
13516     {
13517       int c = ii % 10;
13518
13519       BlitBitmap(src_bitmap, bitmap,
13520                  TILEX + c * 7, 0, 6, 10,
13521                  TILEX * x + 6 + j * 7,
13522                  TILEY * y + 11 + yoffset_ce);
13523
13524       BlitBitmap(src_bitmap, bitmap,
13525                  TILEX + c * 8, TILEY, 6, 10,
13526                  TILEX * 16 + TILEX * x + 6 + j * 8,
13527                  TILEY * y + 10 + yoffset_ce);
13528
13529       ii /= 10;
13530     }
13531   }
13532
13533   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13534   {
13535     int x = i % 16;
13536     int y = i / 16;
13537     int ii = i + 1;
13538     int j;
13539
13540     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13541                TILEX * x, TILEY * y + yoffset_ge);
13542
13543     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13544                TILEX, TILEY,
13545                TILEX * x + TILEX * 16,
13546                TILEY * y + yoffset_ge);
13547
13548     for (j = 1; j >= 0; j--)
13549     {
13550       int c = ii % 10;
13551
13552       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13553                  TILEX * x + 6 + j * 10,
13554                  TILEY * y + 11 + yoffset_ge);
13555
13556       BlitBitmap(src_bitmap, bitmap,
13557                  TILEX + c * 8, TILEY + 12, 6, 10,
13558                  TILEX * 16 + TILEX * x + 10 + j * 8,
13559                  TILEY * y + 10 + yoffset_ge);
13560
13561       ii /= 10;
13562     }
13563   }
13564
13565   if (SDL_SaveBMP(bitmap->surface, filename) != 0)
13566     Error(ERR_EXIT, "cannot save CE graphics file '%s'", filename);
13567
13568   FreeBitmap(bitmap);
13569
13570   CloseAllAndExit(0);
13571 #endif
13572 }
13573
13574 #if 0
13575 void CreateLevelSketchImages_TEST()
13576 {
13577   void CreateCustomElementImages()
13578 }
13579 #endif