rnd-20100309-1-src
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * files.c                                                  *
12 ***********************************************************/
13
14 #include <ctype.h>
15 #include <sys/stat.h>
16 #include <dirent.h>
17 #include <math.h>
18
19 #include "libgame/libgame.h"
20
21 #include "files.h"
22 #include "init.h"
23 #include "tools.h"
24 #include "tape.h"
25
26
27 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
28 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
29 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
30
31 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
32 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
33
34 #define LEVEL_CHUNK_VERS_SIZE   8       /* size of file version chunk */
35 #define LEVEL_CHUNK_DATE_SIZE   4       /* size of file date chunk    */
36 #define LEVEL_CHUNK_HEAD_SIZE   80      /* size of level file header  */
37 #define LEVEL_CHUNK_HEAD_UNUSED 0       /* unused level header bytes  */
38 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
39 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
40 #define LEVEL_CHUNK_CNT3_HEADER 16      /* size of level CNT3 header  */
41 #define LEVEL_CHUNK_CNT3_UNUSED 10      /* unused CNT3 chunk bytes    */
42 #define LEVEL_CPART_CUS3_SIZE   134     /* size of CUS3 chunk part    */
43 #define LEVEL_CPART_CUS3_UNUSED 15      /* unused CUS3 bytes / part   */
44 #define LEVEL_CHUNK_GRP1_SIZE   74      /* size of level GRP1 chunk   */
45
46 /* (element number, number of change pages, change page number) */
47 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
48
49 /* (element number only) */
50 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
51 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
52
53 /* (nothing at all if unchanged) */
54 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
55
56 #define TAPE_CHUNK_VERS_SIZE    8       /* size of file version chunk */
57 #define TAPE_CHUNK_HEAD_SIZE    20      /* size of tape file header   */
58 #define TAPE_CHUNK_HEAD_UNUSED  3       /* unused tape header bytes   */
59
60 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
61 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
62 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
63
64 /* file identifier strings */
65 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
66 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
67 #define SCORE_COOKIE                    "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
68
69 /* values for deciding when (not) to save configuration data */
70 #define SAVE_CONF_NEVER                 0
71 #define SAVE_CONF_ALWAYS                1
72 #define SAVE_CONF_WHEN_CHANGED          -1
73
74 /* values for chunks using micro chunks */
75 #define CONF_MASK_1_BYTE                0x00
76 #define CONF_MASK_2_BYTE                0x40
77 #define CONF_MASK_4_BYTE                0x80
78 #define CONF_MASK_MULTI_BYTES           0xc0
79
80 #define CONF_MASK_BYTES                 0xc0
81 #define CONF_MASK_TOKEN                 0x3f
82
83 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
84 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
85 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
86 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
87
88 /* these definitions are just for convenience of use and readability */
89 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
90 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
91 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
92 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
93
94 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
95                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
96                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
97
98 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
99 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
100 #define CONF_ELEMENT_NUM_BYTES          (2)
101
102 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
103                                          (t) == TYPE_ELEMENT_LIST ?     \
104                                          CONF_ELEMENT_NUM_BYTES :       \
105                                          (t) == TYPE_CONTENT ||         \
106                                          (t) == TYPE_CONTENT_LIST ?     \
107                                          CONF_CONTENT_NUM_BYTES : 1)
108
109 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
110 #define CONF_ELEMENTS_ELEMENT(b,i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) |  \
111                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
112
113 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
114                                          (y) * 3 + (x))
115 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
116                                          CONF_ELEMENT_NUM_BYTES)
117 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
118                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
119
120 /* temporary variables used to store pointers to structure members */
121 static struct LevelInfo li;
122 static struct ElementInfo xx_ei, yy_ei;
123 static struct ElementChangeInfo xx_change;
124 static struct ElementGroupInfo xx_group;
125 static struct EnvelopeInfo xx_envelope;
126 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
127 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
128 static int xx_num_contents;
129 static int xx_current_change_page;
130 static char xx_default_string_empty[1] = "";
131 static int xx_string_length_unused;
132
133 struct LevelFileConfigInfo
134 {
135   int element;                  /* element for which data is to be stored */
136   int save_type;                /* save data always, never or when changed */
137   int data_type;                /* data type (used internally, not stored) */
138   int conf_type;                /* micro chunk identifier (stored in file) */
139
140   /* (mandatory) */
141   void *value;                  /* variable that holds the data to be stored */
142   int default_value;            /* initial default value for this variable */
143
144   /* (optional) */
145   void *value_copy;             /* variable that holds the data to be copied */
146   void *num_entities;           /* number of entities for multi-byte data */
147   int default_num_entities;     /* default number of entities for this data */
148   int max_num_entities;         /* maximal number of entities for this data */
149   char *default_string;         /* optional default string for string data */
150 };
151
152 static struct LevelFileConfigInfo chunk_config_INFO[] =
153 {
154   /* ---------- values not related to single elements ----------------------- */
155
156   {
157     -1,                                 SAVE_CONF_ALWAYS,
158     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
159     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
160   },
161
162   {
163     -1,                                 SAVE_CONF_ALWAYS,
164     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
165     &li.fieldx,                         STD_LEV_FIELDX
166   },
167   {
168     -1,                                 SAVE_CONF_ALWAYS,
169     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
170     &li.fieldy,                         STD_LEV_FIELDY
171   },
172
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
176     &li.time,                           100
177   },
178
179   {
180     -1,                                 SAVE_CONF_ALWAYS,
181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
182     &li.gems_needed,                    0
183   },
184
185   {
186     -1,                                 -1,
187     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
188     &li.random_seed,                    0
189   },
190
191   {
192     -1,                                 -1,
193     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
194     &li.use_step_counter,               FALSE
195   },
196
197   {
198     -1,                                 -1,
199     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
200     &li.wind_direction_initial,         MV_NONE
201   },
202
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208
209   {
210     -1,                                 -1,
211     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
212     &li.use_custom_template,            FALSE
213   },
214
215   {
216     -1,                                 -1,
217     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
218     &li.can_move_into_acid_bits,        ~0      /* default: everything can */
219   },
220
221   {
222     -1,                                 -1,
223     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
224     &li.dont_collide_with_bits,         ~0      /* default: always deadly */
225   },
226
227   {
228     -1,                                 -1,
229     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
230     &li.em_explodes_by_fire,            FALSE
231   },
232
233   {
234     -1,                                 -1,
235     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
236     &li.score[SC_TIME_BONUS],           1
237   },
238
239   {
240     -1,                                 -1,
241     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
242     &li.auto_exit_sokoban,              FALSE
243   },
244
245   {
246     -1,                                 -1,
247     -1,                                 -1,
248     NULL,                               -1
249   }
250 };
251
252 static struct LevelFileConfigInfo chunk_config_ELEM[] =
253 {
254   /* (these values are the same for each player) */
255   {
256     EL_PLAYER_1,                        -1,
257     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
258     &li.block_last_field,               FALSE   /* default case for EM levels */
259   },
260   {
261     EL_PLAYER_1,                        -1,
262     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
263     &li.sp_block_last_field,            TRUE    /* default case for SP levels */
264   },
265   {
266     EL_PLAYER_1,                        -1,
267     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
268     &li.instant_relocation,             FALSE
269   },
270   {
271     EL_PLAYER_1,                        -1,
272     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
273     &li.can_pass_to_walkable,           FALSE
274   },
275   {
276     EL_PLAYER_1,                        -1,
277     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
278     &li.block_snap_field,               TRUE
279   },
280   {
281     EL_PLAYER_1,                        -1,
282     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
283     &li.continuous_snapping,            TRUE
284   },
285   {
286     EL_PLAYER_1,                        -1,
287     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
288     &li.shifted_relocation,             FALSE
289   },
290
291   /* (these values are different for each player) */
292   {
293     EL_PLAYER_1,                        -1,
294     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
295     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
296   },
297   {
298     EL_PLAYER_1,                        -1,
299     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
300     &li.initial_player_gravity[0],      FALSE
301   },
302   {
303     EL_PLAYER_1,                        -1,
304     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
305     &li.use_start_element[0],           FALSE
306   },
307   {
308     EL_PLAYER_1,                        -1,
309     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
310     &li.start_element[0],               EL_PLAYER_1
311   },
312   {
313     EL_PLAYER_1,                        -1,
314     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
315     &li.use_artwork_element[0],         FALSE
316   },
317   {
318     EL_PLAYER_1,                        -1,
319     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
320     &li.artwork_element[0],             EL_PLAYER_1
321   },
322   {
323     EL_PLAYER_1,                        -1,
324     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
325     &li.use_explosion_element[0],       FALSE
326   },
327   {
328     EL_PLAYER_1,                        -1,
329     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
330     &li.explosion_element[0],           EL_PLAYER_1
331   },
332   {
333     EL_PLAYER_1,                        -1,
334     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
335     &li.use_initial_inventory[0],       FALSE
336   },
337   {
338     EL_PLAYER_1,                        -1,
339     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
340     &li.initial_inventory_size[0],      1
341   },
342   {
343     EL_PLAYER_1,                        -1,
344     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
345     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
346     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
347   },
348
349   {
350     EL_PLAYER_2,                        -1,
351     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
352     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
353   },
354   {
355     EL_PLAYER_2,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
357     &li.initial_player_gravity[1],      FALSE
358   },
359   {
360     EL_PLAYER_2,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
362     &li.use_start_element[1],           FALSE
363   },
364   {
365     EL_PLAYER_2,                        -1,
366     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
367     &li.start_element[1],               EL_PLAYER_2
368   },
369   {
370     EL_PLAYER_2,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
372     &li.use_artwork_element[1],         FALSE
373   },
374   {
375     EL_PLAYER_2,                        -1,
376     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
377     &li.artwork_element[1],             EL_PLAYER_2
378   },
379   {
380     EL_PLAYER_2,                        -1,
381     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
382     &li.use_explosion_element[1],       FALSE
383   },
384   {
385     EL_PLAYER_2,                        -1,
386     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
387     &li.explosion_element[1],           EL_PLAYER_2
388   },
389   {
390     EL_PLAYER_2,                        -1,
391     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
392     &li.use_initial_inventory[1],       FALSE
393   },
394   {
395     EL_PLAYER_2,                        -1,
396     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
397     &li.initial_inventory_size[1],      1
398   },
399   {
400     EL_PLAYER_2,                        -1,
401     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
402     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
403     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
404   },
405
406   {
407     EL_PLAYER_3,                        -1,
408     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
409     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
410   },
411   {
412     EL_PLAYER_3,                        -1,
413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
414     &li.initial_player_gravity[2],      FALSE
415   },
416   {
417     EL_PLAYER_3,                        -1,
418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
419     &li.use_start_element[2],           FALSE
420   },
421   {
422     EL_PLAYER_3,                        -1,
423     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
424     &li.start_element[2],               EL_PLAYER_3
425   },
426   {
427     EL_PLAYER_3,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
429     &li.use_artwork_element[2],         FALSE
430   },
431   {
432     EL_PLAYER_3,                        -1,
433     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
434     &li.artwork_element[2],             EL_PLAYER_3
435   },
436   {
437     EL_PLAYER_3,                        -1,
438     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
439     &li.use_explosion_element[2],       FALSE
440   },
441   {
442     EL_PLAYER_3,                        -1,
443     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
444     &li.explosion_element[2],           EL_PLAYER_3
445   },
446   {
447     EL_PLAYER_3,                        -1,
448     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
449     &li.use_initial_inventory[2],       FALSE
450   },
451   {
452     EL_PLAYER_3,                        -1,
453     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
454     &li.initial_inventory_size[2],      1
455   },
456   {
457     EL_PLAYER_3,                        -1,
458     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
459     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
460     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
461   },
462
463   {
464     EL_PLAYER_4,                        -1,
465     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
466     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
467   },
468   {
469     EL_PLAYER_4,                        -1,
470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
471     &li.initial_player_gravity[3],      FALSE
472   },
473   {
474     EL_PLAYER_4,                        -1,
475     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
476     &li.use_start_element[3],           FALSE
477   },
478   {
479     EL_PLAYER_4,                        -1,
480     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
481     &li.start_element[3],               EL_PLAYER_4
482   },
483   {
484     EL_PLAYER_4,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
486     &li.use_artwork_element[3],         FALSE
487   },
488   {
489     EL_PLAYER_4,                        -1,
490     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
491     &li.artwork_element[3],             EL_PLAYER_4
492   },
493   {
494     EL_PLAYER_4,                        -1,
495     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
496     &li.use_explosion_element[3],       FALSE
497   },
498   {
499     EL_PLAYER_4,                        -1,
500     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
501     &li.explosion_element[3],           EL_PLAYER_4
502   },
503   {
504     EL_PLAYER_4,                        -1,
505     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
506     &li.use_initial_inventory[3],       FALSE
507   },
508   {
509     EL_PLAYER_4,                        -1,
510     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
511     &li.initial_inventory_size[3],      1
512   },
513   {
514     EL_PLAYER_4,                        -1,
515     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
516     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
517     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
518   },
519
520   {
521     EL_EMERALD,                         -1,
522     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
523     &li.score[SC_EMERALD],              10
524   },
525
526   {
527     EL_DIAMOND,                         -1,
528     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
529     &li.score[SC_DIAMOND],              10
530   },
531
532   {
533     EL_BUG,                             -1,
534     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
535     &li.score[SC_BUG],                  10
536   },
537
538   {
539     EL_SPACESHIP,                       -1,
540     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
541     &li.score[SC_SPACESHIP],            10
542   },
543
544   {
545     EL_PACMAN,                          -1,
546     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
547     &li.score[SC_PACMAN],               10
548   },
549
550   {
551     EL_NUT,                             -1,
552     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
553     &li.score[SC_NUT],                  10
554   },
555
556   {
557     EL_DYNAMITE,                        -1,
558     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
559     &li.score[SC_DYNAMITE],             10
560   },
561
562   {
563     EL_KEY_1,                           -1,
564     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
565     &li.score[SC_KEY],                  10
566   },
567
568   {
569     EL_PEARL,                           -1,
570     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
571     &li.score[SC_PEARL],                10
572   },
573
574   {
575     EL_CRYSTAL,                         -1,
576     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
577     &li.score[SC_CRYSTAL],              10
578   },
579
580   {
581     EL_BD_AMOEBA,                       -1,
582     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
583     &li.amoeba_content,                 EL_DIAMOND
584   },
585   {
586     EL_BD_AMOEBA,                       -1,
587     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
588     &li.amoeba_speed,                   10
589   },
590   {
591     EL_BD_AMOEBA,                       -1,
592     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
593     &li.grow_into_diggable,             TRUE
594   },
595
596   {
597     EL_YAMYAM,                          -1,
598     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
599     &li.yamyam_content,                 EL_ROCK, NULL,
600     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
601   },
602   {
603     EL_YAMYAM,                          -1,
604     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
605     &li.score[SC_YAMYAM],               10
606   },
607
608   {
609     EL_ROBOT,                           -1,
610     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
611     &li.score[SC_ROBOT],                10
612   },
613   {
614     EL_ROBOT,                           -1,
615     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
616     &li.slurp_score,                    10
617   },
618
619   {
620     EL_ROBOT_WHEEL,                     -1,
621     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
622     &li.time_wheel,                     10
623   },
624
625   {
626     EL_MAGIC_WALL,                      -1,
627     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
628     &li.time_magic_wall,                10
629   },
630
631   {
632     EL_GAME_OF_LIFE,                    -1,
633     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
634     &li.game_of_life[0],                2
635   },
636   {
637     EL_GAME_OF_LIFE,                    -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
639     &li.game_of_life[1],                3
640   },
641   {
642     EL_GAME_OF_LIFE,                    -1,
643     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
644     &li.game_of_life[2],                3
645   },
646   {
647     EL_GAME_OF_LIFE,                    -1,
648     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
649     &li.game_of_life[3],                3
650   },
651
652   {
653     EL_BIOMAZE,                         -1,
654     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
655     &li.biomaze[0],                     2
656   },
657   {
658     EL_BIOMAZE,                         -1,
659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
660     &li.biomaze[1],                     3
661   },
662   {
663     EL_BIOMAZE,                         -1,
664     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
665     &li.biomaze[2],                     3
666   },
667   {
668     EL_BIOMAZE,                         -1,
669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
670     &li.biomaze[3],                     3
671   },
672
673   {
674     EL_TIMEGATE_SWITCH,                 -1,
675     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
676     &li.time_timegate,                  10
677   },
678
679   {
680     EL_LIGHT_SWITCH_ACTIVE,             -1,
681     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
682     &li.time_light,                     10
683   },
684
685   {
686     EL_SHIELD_NORMAL,                   -1,
687     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
688     &li.shield_normal_time,             10
689   },
690   {
691     EL_SHIELD_NORMAL,                   -1,
692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
693     &li.score[SC_SHIELD],               10
694   },
695
696   {
697     EL_SHIELD_DEADLY,                   -1,
698     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
699     &li.shield_deadly_time,             10
700   },
701   {
702     EL_SHIELD_DEADLY,                   -1,
703     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
704     &li.score[SC_SHIELD],               10
705   },
706
707   {
708     EL_EXTRA_TIME,                      -1,
709     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
710     &li.extra_time,                     10
711   },
712   {
713     EL_EXTRA_TIME,                      -1,
714     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
715     &li.extra_time_score,               10
716   },
717
718   {
719     EL_TIME_ORB_FULL,                   -1,
720     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
721     &li.time_orb_time,                  10
722   },
723   {
724     EL_TIME_ORB_FULL,                   -1,
725     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
726     &li.use_time_orb_bug,               FALSE
727   },
728
729   {
730     EL_SPRING,                          -1,
731     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
732     &li.use_spring_bug,                 FALSE
733   },
734
735   {
736     EL_EMC_ANDROID,                     -1,
737     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
738     &li.android_move_time,              10
739   },
740   {
741     EL_EMC_ANDROID,                     -1,
742     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
743     &li.android_clone_time,             10
744   },
745   {
746     EL_EMC_ANDROID,                     -1,
747     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
748     &li.android_clone_element[0],       EL_EMPTY, NULL,
749     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
750   },
751
752   {
753     EL_EMC_LENSES,                      -1,
754     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
755     &li.lenses_score,                   10
756   },
757   {
758     EL_EMC_LENSES,                      -1,
759     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
760     &li.lenses_time,                    10
761   },
762
763   {
764     EL_EMC_MAGNIFIER,                   -1,
765     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
766     &li.magnify_score,                  10
767   },
768   {
769     EL_EMC_MAGNIFIER,                   -1,
770     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
771     &li.magnify_time,                   10
772   },
773
774   {
775     EL_EMC_MAGIC_BALL,                  -1,
776     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
777     &li.ball_time,                      10
778   },
779   {
780     EL_EMC_MAGIC_BALL,                  -1,
781     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
782     &li.ball_random,                    FALSE
783   },
784   {
785     EL_EMC_MAGIC_BALL,                  -1,
786     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
787     &li.ball_state_initial,             FALSE
788   },
789   {
790     EL_EMC_MAGIC_BALL,                  -1,
791     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
792     &li.ball_content,                   EL_EMPTY, NULL,
793     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
794   },
795
796   /* ---------- unused values ----------------------------------------------- */
797
798   {
799     EL_UNKNOWN,                         SAVE_CONF_NEVER,
800     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
801     &li.score[SC_UNKNOWN_14],           10
802   },
803   {
804     EL_UNKNOWN,                         SAVE_CONF_NEVER,
805     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
806     &li.score[SC_UNKNOWN_15],           10
807   },
808
809   {
810     -1,                                 -1,
811     -1,                                 -1,
812     NULL,                               -1
813   }
814 };
815
816 static struct LevelFileConfigInfo chunk_config_NOTE[] =
817 {
818   {
819     -1,                                 -1,
820     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
821     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
822   },
823   {
824     -1,                                 -1,
825     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
826     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
827   },
828
829   {
830     -1,                                 -1,
831     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
832     &xx_envelope.autowrap,              FALSE
833   },
834   {
835     -1,                                 -1,
836     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
837     &xx_envelope.centered,              FALSE
838   },
839
840   {
841     -1,                                 -1,
842     TYPE_STRING,                        CONF_VALUE_BYTES(1),
843     &xx_envelope.text,                  -1, NULL,
844     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
845     &xx_default_string_empty[0]
846   },
847
848   {
849     -1,                                 -1,
850     -1,                                 -1,
851     NULL,                               -1
852   }
853 };
854
855 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
856 {
857   {
858     -1,                                 -1,
859     TYPE_STRING,                        CONF_VALUE_BYTES(1),
860     &xx_ei.description[0],              -1,
861     &yy_ei.description[0],
862     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
863     &xx_default_description[0]
864   },
865
866   {
867     -1,                                 -1,
868     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
869     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
870     &yy_ei.properties[EP_BITFIELD_BASE_NR]
871   },
872 #if 0
873   /* (reserved) */
874   {
875     -1,                                 -1,
876     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
877     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
878     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
879   },
880 #endif
881
882   {
883     -1,                                 -1,
884     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
885     &xx_ei.use_gfx_element,             FALSE,
886     &yy_ei.use_gfx_element
887   },
888   {
889     -1,                                 -1,
890     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
891     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
892     &yy_ei.gfx_element_initial
893   },
894
895   {
896     -1,                                 -1,
897     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
898     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
899     &yy_ei.access_direction
900   },
901
902   {
903     -1,                                 -1,
904     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
905     &xx_ei.collect_score_initial,       10,
906     &yy_ei.collect_score_initial
907   },
908   {
909     -1,                                 -1,
910     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
911     &xx_ei.collect_count_initial,       1,
912     &yy_ei.collect_count_initial
913   },
914
915   {
916     -1,                                 -1,
917     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
918     &xx_ei.ce_value_fixed_initial,      0,
919     &yy_ei.ce_value_fixed_initial
920   },
921   {
922     -1,                                 -1,
923     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
924     &xx_ei.ce_value_random_initial,     0,
925     &yy_ei.ce_value_random_initial
926   },
927   {
928     -1,                                 -1,
929     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
930     &xx_ei.use_last_ce_value,           FALSE,
931     &yy_ei.use_last_ce_value
932   },
933
934   {
935     -1,                                 -1,
936     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
937     &xx_ei.push_delay_fixed,            8,
938     &yy_ei.push_delay_fixed
939   },
940   {
941     -1,                                 -1,
942     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
943     &xx_ei.push_delay_random,           8,
944     &yy_ei.push_delay_random
945   },
946   {
947     -1,                                 -1,
948     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
949     &xx_ei.drop_delay_fixed,            0,
950     &yy_ei.drop_delay_fixed
951   },
952   {
953     -1,                                 -1,
954     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
955     &xx_ei.drop_delay_random,           0,
956     &yy_ei.drop_delay_random
957   },
958   {
959     -1,                                 -1,
960     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
961     &xx_ei.move_delay_fixed,            0,
962     &yy_ei.move_delay_fixed
963   },
964   {
965     -1,                                 -1,
966     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
967     &xx_ei.move_delay_random,           0,
968     &yy_ei.move_delay_random
969   },
970
971   {
972     -1,                                 -1,
973     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
974     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
975     &yy_ei.move_pattern
976   },
977   {
978     -1,                                 -1,
979     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
980     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
981     &yy_ei.move_direction_initial
982   },
983   {
984     -1,                                 -1,
985     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
986     &xx_ei.move_stepsize,               TILEX / 8,
987     &yy_ei.move_stepsize
988   },
989
990   {
991     -1,                                 -1,
992     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
993     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
994     &yy_ei.move_enter_element
995   },
996   {
997     -1,                                 -1,
998     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
999     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1000     &yy_ei.move_leave_element
1001   },
1002   {
1003     -1,                                 -1,
1004     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1005     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1006     &yy_ei.move_leave_type
1007   },
1008
1009   {
1010     -1,                                 -1,
1011     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1012     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1013     &yy_ei.slippery_type
1014   },
1015
1016   {
1017     -1,                                 -1,
1018     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1019     &xx_ei.explosion_type,              EXPLODES_3X3,
1020     &yy_ei.explosion_type
1021   },
1022   {
1023     -1,                                 -1,
1024     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1025     &xx_ei.explosion_delay,             16,
1026     &yy_ei.explosion_delay
1027   },
1028   {
1029     -1,                                 -1,
1030     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1031     &xx_ei.ignition_delay,              8,
1032     &yy_ei.ignition_delay
1033   },
1034
1035   {
1036     -1,                                 -1,
1037     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1038     &xx_ei.content,                     EL_EMPTY_SPACE,
1039     &yy_ei.content,
1040     &xx_num_contents,                   1, 1
1041   },
1042
1043   /* ---------- "num_change_pages" must be the last entry ------------------- */
1044
1045   {
1046     -1,                                 SAVE_CONF_ALWAYS,
1047     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1048     &xx_ei.num_change_pages,            1,
1049     &yy_ei.num_change_pages
1050   },
1051
1052   {
1053     -1,                                 -1,
1054     -1,                                 -1,
1055     NULL,                               -1,
1056     NULL
1057   }
1058 };
1059
1060 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1061 {
1062   /* ---------- "current_change_page" must be the first entry --------------- */
1063
1064   {
1065     -1,                                 SAVE_CONF_ALWAYS,
1066     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1067     &xx_current_change_page,            -1
1068   },
1069
1070   /* ---------- (the remaining entries can be in any order) ----------------- */
1071
1072   {
1073     -1,                                 -1,
1074     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1075     &xx_change.can_change,              FALSE
1076   },
1077
1078   {
1079     -1,                                 -1,
1080     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1081     &xx_event_bits[0],                  0
1082   },
1083   {
1084     -1,                                 -1,
1085     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1086     &xx_event_bits[1],                  0
1087   },
1088
1089   {
1090     -1,                                 -1,
1091     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1092     &xx_change.trigger_player,          CH_PLAYER_ANY
1093   },
1094   {
1095     -1,                                 -1,
1096     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1097     &xx_change.trigger_side,            CH_SIDE_ANY
1098   },
1099   {
1100     -1,                                 -1,
1101     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1102     &xx_change.trigger_page,            CH_PAGE_ANY
1103   },
1104
1105   {
1106     -1,                                 -1,
1107     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1108     &xx_change.target_element,          EL_EMPTY_SPACE
1109   },
1110
1111   {
1112     -1,                                 -1,
1113     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1114     &xx_change.delay_fixed,             0
1115   },
1116   {
1117     -1,                                 -1,
1118     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1119     &xx_change.delay_random,            0
1120   },
1121   {
1122     -1,                                 -1,
1123     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1124     &xx_change.delay_frames,            FRAMES_PER_SECOND
1125   },
1126
1127   {
1128     -1,                                 -1,
1129     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1130     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1131   },
1132
1133   {
1134     -1,                                 -1,
1135     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1136     &xx_change.explode,                 FALSE
1137   },
1138   {
1139     -1,                                 -1,
1140     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1141     &xx_change.use_target_content,      FALSE
1142   },
1143   {
1144     -1,                                 -1,
1145     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1146     &xx_change.only_if_complete,        FALSE
1147   },
1148   {
1149     -1,                                 -1,
1150     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1151     &xx_change.use_random_replace,      FALSE
1152   },
1153   {
1154     -1,                                 -1,
1155     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1156     &xx_change.random_percentage,       100
1157   },
1158   {
1159     -1,                                 -1,
1160     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1161     &xx_change.replace_when,            CP_WHEN_EMPTY
1162   },
1163
1164   {
1165     -1,                                 -1,
1166     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1167     &xx_change.has_action,              FALSE
1168   },
1169   {
1170     -1,                                 -1,
1171     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1172     &xx_change.action_type,             CA_NO_ACTION
1173   },
1174   {
1175     -1,                                 -1,
1176     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1177     &xx_change.action_mode,             CA_MODE_UNDEFINED
1178   },
1179   {
1180     -1,                                 -1,
1181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1182     &xx_change.action_arg,              CA_ARG_UNDEFINED
1183   },
1184
1185   {
1186     -1,                                 -1,
1187     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1188     &xx_change.action_element,          EL_EMPTY_SPACE
1189   },
1190
1191   {
1192     -1,                                 -1,
1193     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1194     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1195     &xx_num_contents,                   1, 1
1196   },
1197
1198   {
1199     -1,                                 -1,
1200     -1,                                 -1,
1201     NULL,                               -1
1202   }
1203 };
1204
1205 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1206 {
1207   {
1208     -1,                                 -1,
1209     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1210     &xx_ei.description[0],              -1, NULL,
1211     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1212     &xx_default_description[0]
1213   },
1214
1215   {
1216     -1,                                 -1,
1217     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1218     &xx_ei.use_gfx_element,             FALSE
1219   },
1220   {
1221     -1,                                 -1,
1222     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1223     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1224   },
1225
1226   {
1227     -1,                                 -1,
1228     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1229     &xx_group.choice_mode,              ANIM_RANDOM
1230   },
1231
1232   {
1233     -1,                                 -1,
1234     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1235     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1236     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1237   },
1238
1239   {
1240     -1,                                 -1,
1241     -1,                                 -1,
1242     NULL,                               -1
1243   }
1244 };
1245
1246 static struct LevelFileConfigInfo chunk_config_CONF[] =         /* (OBSOLETE) */
1247 {
1248   {
1249     EL_PLAYER_1,                        -1,
1250     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1251     &li.block_snap_field,               TRUE
1252   },
1253   {
1254     EL_PLAYER_1,                        -1,
1255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1256     &li.continuous_snapping,            TRUE
1257   },
1258   {
1259     EL_PLAYER_1,                        -1,
1260     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1261     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1262   },
1263   {
1264     EL_PLAYER_1,                        -1,
1265     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1266     &li.use_start_element[0],           FALSE
1267   },
1268   {
1269     EL_PLAYER_1,                        -1,
1270     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1271     &li.start_element[0],               EL_PLAYER_1
1272   },
1273   {
1274     EL_PLAYER_1,                        -1,
1275     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1276     &li.use_artwork_element[0],         FALSE
1277   },
1278   {
1279     EL_PLAYER_1,                        -1,
1280     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1281     &li.artwork_element[0],             EL_PLAYER_1
1282   },
1283   {
1284     EL_PLAYER_1,                        -1,
1285     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1286     &li.use_explosion_element[0],       FALSE
1287   },
1288   {
1289     EL_PLAYER_1,                        -1,
1290     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1291     &li.explosion_element[0],           EL_PLAYER_1
1292   },
1293
1294   {
1295     -1,                                 -1,
1296     -1,                                 -1,
1297     NULL,                               -1
1298   }
1299 };
1300
1301 static struct
1302 {
1303   int filetype;
1304   char *id;
1305 }
1306 filetype_id_list[] =
1307 {
1308   { LEVEL_FILE_TYPE_RND,        "RND"   },
1309   { LEVEL_FILE_TYPE_BD,         "BD"    },
1310   { LEVEL_FILE_TYPE_EM,         "EM"    },
1311   { LEVEL_FILE_TYPE_SP,         "SP"    },
1312   { LEVEL_FILE_TYPE_DX,         "DX"    },
1313   { LEVEL_FILE_TYPE_SB,         "SB"    },
1314   { LEVEL_FILE_TYPE_DC,         "DC"    },
1315   { -1,                         NULL    },
1316 };
1317
1318
1319 /* ========================================================================= */
1320 /* level file functions                                                      */
1321 /* ========================================================================= */
1322
1323 static boolean check_special_flags(char *flag)
1324 {
1325 #if 0
1326   printf("::: '%s', '%s', '%s'\n",
1327          flag,
1328          options.special_flags,
1329          leveldir_current->special_flags);
1330 #endif
1331
1332   if (strEqual(options.special_flags, flag) ||
1333       strEqual(leveldir_current->special_flags, flag))
1334     return TRUE;
1335
1336   return FALSE;
1337 }
1338
1339 static struct DateInfo getCurrentDate()
1340 {
1341   time_t epoch_seconds = time(NULL);
1342   struct tm *now = localtime(&epoch_seconds);
1343   struct DateInfo date;
1344
1345   date.year  = now->tm_year + 1900;
1346   date.month = now->tm_mon  + 1;
1347   date.day   = now->tm_mday;
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 static void setLevelInfoToDefaults(struct LevelInfo *level)
1598 {
1599   static boolean clipboard_elements_initialized = FALSE;
1600   int i, x, y;
1601
1602   InitElementPropertiesStatic();
1603
1604   li = *level;          /* copy level data into temporary buffer */
1605
1606   setConfigToDefaultsFromConfigList(chunk_config_INFO);
1607   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1608
1609   *level = li;          /* copy temporary buffer back to level data */
1610
1611   setLevelInfoToDefaults_EM();
1612   setLevelInfoToDefaults_SP();
1613
1614   level->native_em_level = &native_em_level;
1615   level->native_sp_level = &native_sp_level;
1616
1617   level->file_version = FILE_VERSION_ACTUAL;
1618   level->game_version = GAME_VERSION_ACTUAL;
1619
1620   level->creation_date = getCurrentDate();
1621
1622   level->encoding_16bit_field  = TRUE;
1623   level->encoding_16bit_yamyam = TRUE;
1624   level->encoding_16bit_amoeba = TRUE;
1625
1626   for (x = 0; x < MAX_LEV_FIELDX; x++)
1627     for (y = 0; y < MAX_LEV_FIELDY; y++)
1628       level->field[x][y] = EL_SAND;
1629
1630   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1631     level->name[i] = '\0';
1632   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1633     level->author[i] = '\0';
1634
1635   strcpy(level->name, NAMELESS_LEVEL_NAME);
1636   strcpy(level->author, ANONYMOUS_NAME);
1637
1638   level->field[0][0] = EL_PLAYER_1;
1639   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1640
1641   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1642   {
1643     int element = i;
1644     struct ElementInfo *ei = &element_info[element];
1645
1646     /* never initialize clipboard elements after the very first time */
1647     /* (to be able to use clipboard elements between several levels) */
1648     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1649       continue;
1650
1651     if (IS_ENVELOPE(element))
1652     {
1653       int envelope_nr = element - EL_ENVELOPE_1;
1654
1655       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1656
1657       level->envelope[envelope_nr] = xx_envelope;
1658     }
1659
1660     if (IS_CUSTOM_ELEMENT(element) ||
1661         IS_GROUP_ELEMENT(element) ||
1662         IS_INTERNAL_ELEMENT(element))
1663     {
1664       xx_ei = *ei;      /* copy element data into temporary buffer */
1665
1666       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1667
1668       *ei = xx_ei;
1669     }
1670
1671     setElementChangePages(ei, 1);
1672     setElementChangeInfoToDefaults(ei->change);
1673
1674     if (IS_CUSTOM_ELEMENT(element) ||
1675         IS_GROUP_ELEMENT(element) ||
1676         IS_INTERNAL_ELEMENT(element))
1677     {
1678       setElementDescriptionToDefault(ei);
1679
1680       ei->modified_settings = FALSE;
1681     }
1682
1683     if (IS_CUSTOM_ELEMENT(element) ||
1684         IS_INTERNAL_ELEMENT(element))
1685     {
1686       /* internal values used in level editor */
1687
1688       ei->access_type = 0;
1689       ei->access_layer = 0;
1690       ei->access_protected = 0;
1691       ei->walk_to_action = 0;
1692       ei->smash_targets = 0;
1693       ei->deadliness = 0;
1694
1695       ei->can_explode_by_fire = FALSE;
1696       ei->can_explode_smashed = FALSE;
1697       ei->can_explode_impact = FALSE;
1698
1699       ei->current_change_page = 0;
1700     }
1701
1702     if (IS_GROUP_ELEMENT(element) ||
1703         IS_INTERNAL_ELEMENT(element))
1704     {
1705       struct ElementGroupInfo *group;
1706
1707       /* initialize memory for list of elements in group */
1708       if (ei->group == NULL)
1709         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1710
1711       group = ei->group;
1712
1713       xx_group = *group;        /* copy group data into temporary buffer */
1714
1715       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1716
1717       *group = xx_group;
1718     }
1719   }
1720
1721   clipboard_elements_initialized = TRUE;
1722
1723   BorderElement = EL_STEELWALL;
1724
1725   level->no_valid_file = FALSE;
1726
1727   level->changed = FALSE;
1728
1729   /* set all bug compatibility flags to "false" => do not emulate this bug */
1730   level->use_action_after_change_bug = FALSE;
1731
1732   if (leveldir_current == NULL)         /* only when dumping level */
1733     return;
1734
1735   /* try to determine better author name than 'anonymous' */
1736   if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1737   {
1738     strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1739     level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1740   }
1741   else
1742   {
1743     switch (LEVELCLASS(leveldir_current))
1744     {
1745       case LEVELCLASS_TUTORIAL:
1746         strcpy(level->author, PROGRAM_AUTHOR_STRING);
1747         break;
1748
1749       case LEVELCLASS_CONTRIB:
1750         strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1751         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1752         break;
1753
1754       case LEVELCLASS_PRIVATE:
1755         strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1756         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1757         break;
1758
1759       default:
1760         /* keep default value */
1761         break;
1762     }
1763   }
1764 }
1765
1766 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1767 {
1768   level_file_info->nr = 0;
1769   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1770   level_file_info->packed = FALSE;
1771   level_file_info->basename = NULL;
1772   level_file_info->filename = NULL;
1773 }
1774
1775 static void ActivateLevelTemplate()
1776 {
1777   int x, y;
1778
1779   /* Currently there is no special action needed to activate the template
1780      data, because 'element_info' property settings overwrite the original
1781      level data, while all other variables do not change. */
1782
1783   /* Exception: 'from_level_template' elements in the original level playfield
1784      are overwritten with the corresponding elements at the same position in
1785      playfield from the level template. */
1786
1787   for (x = 0; x < level.fieldx; x++)
1788     for (y = 0; y < level.fieldy; y++)
1789       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1790         level.field[x][y] = level_template.field[x][y];
1791
1792   if (check_special_flags("load_xsb_to_ces"))
1793   {
1794     struct LevelInfo level_backup = level;
1795
1796     /* overwrite all individual level settings from template level settings */
1797     level = level_template;
1798
1799     /* restore playfield size */
1800     level.fieldx = level_backup.fieldx;
1801     level.fieldy = level_backup.fieldy;
1802
1803     /* restore playfield content */
1804     for (x = 0; x < level.fieldx; x++)
1805       for (y = 0; y < level.fieldy; y++)
1806         level.field[x][y] = level_backup.field[x][y];
1807
1808     /* restore name and author from individual level */
1809     strcpy(level.name,   level_backup.name);
1810     strcpy(level.author, level_backup.author);
1811
1812     /* restore flag "use_custom_template" */
1813     level.use_custom_template = level_backup.use_custom_template;
1814   }
1815 }
1816
1817 static char *getLevelFilenameFromBasename(char *basename)
1818 {
1819   static char *filename = NULL;
1820
1821   checked_free(filename);
1822
1823   filename = getPath2(getCurrentLevelDir(), basename);
1824
1825   return filename;
1826 }
1827
1828 static int getFileTypeFromBasename(char *basename)
1829 {
1830   /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1831
1832   static char *filename = NULL;
1833   struct stat file_status;
1834
1835   /* ---------- try to determine file type from filename ---------- */
1836
1837   /* check for typical filename of a Supaplex level package file */
1838 #if 1
1839   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1840     return LEVEL_FILE_TYPE_SP;
1841 #else
1842   if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
1843                                  strncmp(basename, "LEVELS.D", 8) == 0))
1844     return LEVEL_FILE_TYPE_SP;
1845 #endif
1846
1847   /* check for typical filename of a Diamond Caves II level package file */
1848   if (strSuffixLower(basename, ".dc") ||
1849       strSuffixLower(basename, ".dc2"))
1850     return LEVEL_FILE_TYPE_DC;
1851
1852   /* check for typical filename of a Sokoban level package file */
1853   if (strSuffixLower(basename, ".xsb") &&
1854       strchr(basename, '%') == NULL)
1855     return LEVEL_FILE_TYPE_SB;
1856
1857   /* ---------- try to determine file type from filesize ---------- */
1858
1859   checked_free(filename);
1860   filename = getPath2(getCurrentLevelDir(), basename);
1861
1862   if (stat(filename, &file_status) == 0)
1863   {
1864     /* check for typical filesize of a Supaplex level package file */
1865     if (file_status.st_size == 170496)
1866       return LEVEL_FILE_TYPE_SP;
1867   }
1868
1869   return LEVEL_FILE_TYPE_UNKNOWN;
1870 }
1871
1872 static boolean checkForPackageFromBasename(char *basename)
1873 {
1874   /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1875      !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!! */
1876
1877   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1878 }
1879
1880 static char *getSingleLevelBasenameExt(int nr, char *extension)
1881 {
1882   static char basename[MAX_FILENAME_LEN];
1883
1884   if (nr < 0)
1885     sprintf(basename, "template.%s", extension);
1886   else
1887     sprintf(basename, "%03d.%s", nr, extension);
1888
1889   return basename;
1890 }
1891
1892 static char *getSingleLevelBasename(int nr)
1893 {
1894   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
1895 }
1896
1897 static char *getPackedLevelBasename(int type)
1898 {
1899   static char basename[MAX_FILENAME_LEN];
1900   char *directory = getCurrentLevelDir();
1901   DIR *dir;
1902   struct dirent *dir_entry;
1903
1904   strcpy(basename, UNDEFINED_FILENAME);         /* default: undefined file */
1905
1906   if ((dir = opendir(directory)) == NULL)
1907   {
1908     Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1909
1910     return basename;
1911   }
1912
1913   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
1914   {
1915     char *entry_basename = dir_entry->d_name;
1916     int entry_type = getFileTypeFromBasename(entry_basename);
1917
1918     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  /* found valid level package */
1919     {
1920       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1921           type == entry_type)
1922       {
1923         strcpy(basename, entry_basename);
1924
1925         break;
1926       }
1927     }
1928   }
1929
1930   closedir(dir);
1931
1932   return basename;
1933 }
1934
1935 static char *getSingleLevelFilename(int nr)
1936 {
1937   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1938 }
1939
1940 #if 0
1941 static char *getPackedLevelFilename(int type)
1942 {
1943   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1944 }
1945 #endif
1946
1947 char *getDefaultLevelFilename(int nr)
1948 {
1949   return getSingleLevelFilename(nr);
1950 }
1951
1952 #if 0
1953 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
1954                                                  int type)
1955 {
1956   lfi->type = type;
1957   lfi->packed = FALSE;
1958   lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
1959   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1960 }
1961 #endif
1962
1963 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
1964                                                  int type, char *format, ...)
1965 {
1966   static char basename[MAX_FILENAME_LEN];
1967   va_list ap;
1968
1969   va_start(ap, format);
1970   vsprintf(basename, format, ap);
1971   va_end(ap);
1972
1973   lfi->type = type;
1974   lfi->packed = FALSE;
1975   lfi->basename = basename;
1976   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1977 }
1978
1979 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
1980                                                  int type)
1981 {
1982   lfi->type = type;
1983   lfi->packed = TRUE;
1984   lfi->basename = getPackedLevelBasename(lfi->type);
1985   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
1986 }
1987
1988 static int getFiletypeFromID(char *filetype_id)
1989 {
1990   char *filetype_id_lower;
1991   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
1992   int i;
1993
1994   if (filetype_id == NULL)
1995     return LEVEL_FILE_TYPE_UNKNOWN;
1996
1997   filetype_id_lower = getStringToLower(filetype_id);
1998
1999   for (i = 0; filetype_id_list[i].id != NULL; i++)
2000   {
2001     char *id_lower = getStringToLower(filetype_id_list[i].id);
2002     
2003     if (strEqual(filetype_id_lower, id_lower))
2004       filetype = filetype_id_list[i].filetype;
2005
2006     free(id_lower);
2007
2008     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2009       break;
2010   }
2011
2012   free(filetype_id_lower);
2013
2014   return filetype;
2015 }
2016
2017 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2018 {
2019   int nr = lfi->nr;
2020
2021   /* special case: level number is negative => check for level template file */
2022   if (nr < 0)
2023   {
2024 #if 1
2025     /* global variable "leveldir_current" must be modified in the loop below */
2026     LevelDirTree *leveldir_current_last = leveldir_current;
2027
2028     /* check for template level in path from current to topmost tree node */
2029
2030     while (leveldir_current != NULL)
2031     {
2032       setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2033                                            "template.%s", LEVELFILE_EXTENSION);
2034
2035       if (fileExists(lfi->filename))
2036         break;
2037
2038       leveldir_current = leveldir_current->node_parent;
2039     }
2040
2041     /* restore global variable "leveldir_current" modified in above loop */
2042     leveldir_current = leveldir_current_last;
2043
2044 #else
2045
2046     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2047                                          "template.%s", LEVELFILE_EXTENSION);
2048
2049 #endif
2050
2051     /* no fallback if template file not existing */
2052     return;
2053   }
2054
2055   /* special case: check for file name/pattern specified in "levelinfo.conf" */
2056   if (leveldir_current->level_filename != NULL)
2057   {
2058     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2059
2060     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2061                                          leveldir_current->level_filename, nr);
2062
2063     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2064
2065     if (fileExists(lfi->filename))
2066       return;
2067   }
2068
2069   /* check for native Rocks'n'Diamonds level file */
2070   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2071                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2072   if (fileExists(lfi->filename))
2073     return;
2074
2075   /* check for Emerald Mine level file (V1) */
2076   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2077                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2078   if (fileExists(lfi->filename))
2079     return;
2080   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2081                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2082   if (fileExists(lfi->filename))
2083     return;
2084
2085   /* check for Emerald Mine level file (V2 to V5) */
2086   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2087   if (fileExists(lfi->filename))
2088     return;
2089
2090   /* check for Emerald Mine level file (V6 / single mode) */
2091   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2092   if (fileExists(lfi->filename))
2093     return;
2094   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2095   if (fileExists(lfi->filename))
2096     return;
2097
2098   /* check for Emerald Mine level file (V6 / teamwork mode) */
2099   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2100   if (fileExists(lfi->filename))
2101     return;
2102   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2103   if (fileExists(lfi->filename))
2104     return;
2105
2106   /* check for various packed level file formats */
2107   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2108   if (fileExists(lfi->filename))
2109     return;
2110
2111   /* no known level file found -- use default values (and fail later) */
2112   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2113                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2114 }
2115
2116 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2117 {
2118   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2119     lfi->type = getFileTypeFromBasename(lfi->basename);
2120 }
2121
2122 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2123 {
2124   /* always start with reliable default values */
2125   setFileInfoToDefaults(level_file_info);
2126
2127   level_file_info->nr = nr;     /* set requested level number */
2128
2129   determineLevelFileInfo_Filename(level_file_info);
2130   determineLevelFileInfo_Filetype(level_file_info);
2131 }
2132
2133 /* ------------------------------------------------------------------------- */
2134 /* functions for loading R'n'D level                                         */
2135 /* ------------------------------------------------------------------------- */
2136
2137 int getMappedElement(int element)
2138 {
2139   /* remap some (historic, now obsolete) elements */
2140
2141   switch (element)
2142   {
2143     case EL_PLAYER_OBSOLETE:
2144       element = EL_PLAYER_1;
2145       break;
2146
2147     case EL_KEY_OBSOLETE:
2148       element = EL_KEY_1;
2149       break;
2150
2151     case EL_EM_KEY_1_FILE_OBSOLETE:
2152       element = EL_EM_KEY_1;
2153       break;
2154
2155     case EL_EM_KEY_2_FILE_OBSOLETE:
2156       element = EL_EM_KEY_2;
2157       break;
2158
2159     case EL_EM_KEY_3_FILE_OBSOLETE:
2160       element = EL_EM_KEY_3;
2161       break;
2162
2163     case EL_EM_KEY_4_FILE_OBSOLETE:
2164       element = EL_EM_KEY_4;
2165       break;
2166
2167     case EL_ENVELOPE_OBSOLETE:
2168       element = EL_ENVELOPE_1;
2169       break;
2170
2171     case EL_SP_EMPTY:
2172       element = EL_EMPTY;
2173       break;
2174
2175     default:
2176       if (element >= NUM_FILE_ELEMENTS)
2177       {
2178         Error(ERR_WARN, "invalid level element %d", element);
2179
2180         element = EL_UNKNOWN;
2181       }
2182       break;
2183   }
2184
2185   return element;
2186 }
2187
2188 int getMappedElementByVersion(int element, int game_version)
2189 {
2190   /* remap some elements due to certain game version */
2191
2192   if (game_version <= VERSION_IDENT(2,2,0,0))
2193   {
2194     /* map game font elements */
2195     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2196                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2197                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2198                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2199   }
2200
2201   if (game_version < VERSION_IDENT(3,0,0,0))
2202   {
2203     /* map Supaplex gravity tube elements */
2204     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2205                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2206                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2207                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2208                element);
2209   }
2210
2211   return element;
2212 }
2213
2214 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
2215 {
2216   level->file_version = getFileVersion(file);
2217   level->game_version = getFileVersion(file);
2218
2219   return chunk_size;
2220 }
2221
2222 static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
2223 {
2224   level->creation_date.year  = getFile16BitBE(file);
2225   level->creation_date.month = getFile8Bit(file);
2226   level->creation_date.day   = getFile8Bit(file);
2227
2228   return chunk_size;
2229 }
2230
2231 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
2232 {
2233   int initial_player_stepsize;
2234   int initial_player_gravity;
2235   int i, x, y;
2236
2237   level->fieldx = getFile8Bit(file);
2238   level->fieldy = getFile8Bit(file);
2239
2240   level->time           = getFile16BitBE(file);
2241   level->gems_needed    = getFile16BitBE(file);
2242
2243   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2244     level->name[i] = getFile8Bit(file);
2245   level->name[MAX_LEVEL_NAME_LEN] = 0;
2246
2247   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2248     level->score[i] = getFile8Bit(file);
2249
2250   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2251   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2252     for (y = 0; y < 3; y++)
2253       for (x = 0; x < 3; x++)
2254         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2255
2256   level->amoeba_speed           = getFile8Bit(file);
2257   level->time_magic_wall        = getFile8Bit(file);
2258   level->time_wheel             = getFile8Bit(file);
2259   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2260
2261   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2262                                    STEPSIZE_NORMAL);
2263
2264   for (i = 0; i < MAX_PLAYERS; i++)
2265     level->initial_player_stepsize[i] = initial_player_stepsize;
2266
2267   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2268
2269   for (i = 0; i < MAX_PLAYERS; i++)
2270     level->initial_player_gravity[i] = initial_player_gravity;
2271
2272   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2273   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2274
2275   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2276
2277   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2278   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2279   level->can_move_into_acid_bits = getFile32BitBE(file);
2280   level->dont_collide_with_bits = getFile8Bit(file);
2281
2282   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2283   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2284
2285   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2286   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2287   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2288
2289   level->game_engine_type       = getFile8Bit(file);
2290
2291   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2292
2293   return chunk_size;
2294 }
2295
2296 static int LoadLevel_NAME(FILE *file, int chunk_size, struct LevelInfo *level)
2297 {
2298   int i;
2299
2300   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2301     level->name[i] = getFile8Bit(file);
2302   level->name[MAX_LEVEL_NAME_LEN] = 0;
2303
2304   return chunk_size;
2305 }
2306
2307 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
2308 {
2309   int i;
2310
2311   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2312     level->author[i] = getFile8Bit(file);
2313   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2314
2315   return chunk_size;
2316 }
2317
2318 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
2319 {
2320   int x, y;
2321   int chunk_size_expected = level->fieldx * level->fieldy;
2322
2323   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2324      stored with 16-bit encoding (and should be twice as big then).
2325      Even worse, playfield data was stored 16-bit when only yamyam content
2326      contained 16-bit elements and vice versa. */
2327
2328   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2329     chunk_size_expected *= 2;
2330
2331   if (chunk_size_expected != chunk_size)
2332   {
2333     ReadUnusedBytesFromFile(file, chunk_size);
2334     return chunk_size_expected;
2335   }
2336
2337   for (y = 0; y < level->fieldy; y++)
2338     for (x = 0; x < level->fieldx; x++)
2339       level->field[x][y] =
2340         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2341                          getFile8Bit(file));
2342   return chunk_size;
2343 }
2344
2345 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
2346 {
2347   int i, x, y;
2348   int header_size = 4;
2349   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2350   int chunk_size_expected = header_size + content_size;
2351
2352   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2353      stored with 16-bit encoding (and should be twice as big then).
2354      Even worse, playfield data was stored 16-bit when only yamyam content
2355      contained 16-bit elements and vice versa. */
2356
2357   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2358     chunk_size_expected += content_size;
2359
2360   if (chunk_size_expected != chunk_size)
2361   {
2362     ReadUnusedBytesFromFile(file, chunk_size);
2363     return chunk_size_expected;
2364   }
2365
2366   getFile8Bit(file);
2367   level->num_yamyam_contents = getFile8Bit(file);
2368   getFile8Bit(file);
2369   getFile8Bit(file);
2370
2371   /* correct invalid number of content fields -- should never happen */
2372   if (level->num_yamyam_contents < 1 ||
2373       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2374     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2375
2376   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2377     for (y = 0; y < 3; y++)
2378       for (x = 0; x < 3; x++)
2379         level->yamyam_content[i].e[x][y] =
2380           getMappedElement(level->encoding_16bit_field ?
2381                            getFile16BitBE(file) : getFile8Bit(file));
2382   return chunk_size;
2383 }
2384
2385 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
2386 {
2387   int i, x, y;
2388   int element;
2389   int num_contents, content_xsize, content_ysize;
2390   int content_array[MAX_ELEMENT_CONTENTS][3][3];
2391
2392   element = getMappedElement(getFile16BitBE(file));
2393   num_contents = getFile8Bit(file);
2394   content_xsize = getFile8Bit(file);
2395   content_ysize = getFile8Bit(file);
2396
2397   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2398
2399   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2400     for (y = 0; y < 3; y++)
2401       for (x = 0; x < 3; x++)
2402         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2403
2404   /* correct invalid number of content fields -- should never happen */
2405   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2406     num_contents = STD_ELEMENT_CONTENTS;
2407
2408   if (element == EL_YAMYAM)
2409   {
2410     level->num_yamyam_contents = num_contents;
2411
2412     for (i = 0; i < num_contents; i++)
2413       for (y = 0; y < 3; y++)
2414         for (x = 0; x < 3; x++)
2415           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2416   }
2417   else if (element == EL_BD_AMOEBA)
2418   {
2419     level->amoeba_content = content_array[0][0][0];
2420   }
2421   else
2422   {
2423     Error(ERR_WARN, "cannot load content for element '%d'", element);
2424   }
2425
2426   return chunk_size;
2427 }
2428
2429 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
2430 {
2431   int i;
2432   int element;
2433   int envelope_nr;
2434   int envelope_len;
2435   int chunk_size_expected;
2436
2437   element = getMappedElement(getFile16BitBE(file));
2438   if (!IS_ENVELOPE(element))
2439     element = EL_ENVELOPE_1;
2440
2441   envelope_nr = element - EL_ENVELOPE_1;
2442
2443   envelope_len = getFile16BitBE(file);
2444
2445   level->envelope[envelope_nr].xsize = getFile8Bit(file);
2446   level->envelope[envelope_nr].ysize = getFile8Bit(file);
2447
2448   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2449
2450   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2451   if (chunk_size_expected != chunk_size)
2452   {
2453     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2454     return chunk_size_expected;
2455   }
2456
2457   for (i = 0; i < envelope_len; i++)
2458     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2459
2460   return chunk_size;
2461 }
2462
2463 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
2464 {
2465   int num_changed_custom_elements = getFile16BitBE(file);
2466   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2467   int i;
2468
2469   if (chunk_size_expected != chunk_size)
2470   {
2471     ReadUnusedBytesFromFile(file, chunk_size - 2);
2472     return chunk_size_expected;
2473   }
2474
2475   for (i = 0; i < num_changed_custom_elements; i++)
2476   {
2477     int element = getMappedElement(getFile16BitBE(file));
2478     int properties = getFile32BitBE(file);
2479
2480     if (IS_CUSTOM_ELEMENT(element))
2481       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2482     else
2483       Error(ERR_WARN, "invalid custom element number %d", element);
2484
2485     /* older game versions that wrote level files with CUS1 chunks used
2486        different default push delay values (not yet stored in level file) */
2487     element_info[element].push_delay_fixed = 2;
2488     element_info[element].push_delay_random = 8;
2489   }
2490
2491   return chunk_size;
2492 }
2493
2494 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
2495 {
2496   int num_changed_custom_elements = getFile16BitBE(file);
2497   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2498   int i;
2499
2500   if (chunk_size_expected != chunk_size)
2501   {
2502     ReadUnusedBytesFromFile(file, chunk_size - 2);
2503     return chunk_size_expected;
2504   }
2505
2506   for (i = 0; i < num_changed_custom_elements; i++)
2507   {
2508     int element = getMappedElement(getFile16BitBE(file));
2509     int custom_target_element = getMappedElement(getFile16BitBE(file));
2510
2511     if (IS_CUSTOM_ELEMENT(element))
2512       element_info[element].change->target_element = custom_target_element;
2513     else
2514       Error(ERR_WARN, "invalid custom element number %d", element);
2515   }
2516
2517   return chunk_size;
2518 }
2519
2520 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
2521 {
2522   int num_changed_custom_elements = getFile16BitBE(file);
2523   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2524   int i, j, x, y;
2525
2526   if (chunk_size_expected != chunk_size)
2527   {
2528     ReadUnusedBytesFromFile(file, chunk_size - 2);
2529     return chunk_size_expected;
2530   }
2531
2532   for (i = 0; i < num_changed_custom_elements; i++)
2533   {
2534     int element = getMappedElement(getFile16BitBE(file));
2535     struct ElementInfo *ei = &element_info[element];
2536     unsigned int event_bits;
2537
2538     if (!IS_CUSTOM_ELEMENT(element))
2539     {
2540       Error(ERR_WARN, "invalid custom element number %d", element);
2541
2542       element = EL_INTERNAL_DUMMY;
2543     }
2544
2545     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2546       ei->description[j] = getFile8Bit(file);
2547     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2548
2549     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2550
2551     /* some free bytes for future properties and padding */
2552     ReadUnusedBytesFromFile(file, 7);
2553
2554     ei->use_gfx_element = getFile8Bit(file);
2555     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2556
2557     ei->collect_score_initial = getFile8Bit(file);
2558     ei->collect_count_initial = getFile8Bit(file);
2559
2560     ei->push_delay_fixed = getFile16BitBE(file);
2561     ei->push_delay_random = getFile16BitBE(file);
2562     ei->move_delay_fixed = getFile16BitBE(file);
2563     ei->move_delay_random = getFile16BitBE(file);
2564
2565     ei->move_pattern = getFile16BitBE(file);
2566     ei->move_direction_initial = getFile8Bit(file);
2567     ei->move_stepsize = getFile8Bit(file);
2568
2569     for (y = 0; y < 3; y++)
2570       for (x = 0; x < 3; x++)
2571         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2572
2573     event_bits = getFile32BitBE(file);
2574     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2575       if (event_bits & (1 << j))
2576         ei->change->has_event[j] = TRUE;
2577
2578     ei->change->target_element = getMappedElement(getFile16BitBE(file));
2579
2580     ei->change->delay_fixed = getFile16BitBE(file);
2581     ei->change->delay_random = getFile16BitBE(file);
2582     ei->change->delay_frames = getFile16BitBE(file);
2583
2584     ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2585
2586     ei->change->explode = getFile8Bit(file);
2587     ei->change->use_target_content = getFile8Bit(file);
2588     ei->change->only_if_complete = getFile8Bit(file);
2589     ei->change->use_random_replace = getFile8Bit(file);
2590
2591     ei->change->random_percentage = getFile8Bit(file);
2592     ei->change->replace_when = getFile8Bit(file);
2593
2594     for (y = 0; y < 3; y++)
2595       for (x = 0; x < 3; x++)
2596         ei->change->target_content.e[x][y] =
2597           getMappedElement(getFile16BitBE(file));
2598
2599     ei->slippery_type = getFile8Bit(file);
2600
2601     /* some free bytes for future properties and padding */
2602     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2603
2604     /* mark that this custom element has been modified */
2605     ei->modified_settings = TRUE;
2606   }
2607
2608   return chunk_size;
2609 }
2610
2611 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
2612 {
2613   struct ElementInfo *ei;
2614   int chunk_size_expected;
2615   int element;
2616   int i, j, x, y;
2617
2618   /* ---------- custom element base property values (96 bytes) ------------- */
2619
2620   element = getMappedElement(getFile16BitBE(file));
2621
2622   if (!IS_CUSTOM_ELEMENT(element))
2623   {
2624     Error(ERR_WARN, "invalid custom element number %d", element);
2625
2626     ReadUnusedBytesFromFile(file, chunk_size - 2);
2627     return chunk_size;
2628   }
2629
2630   ei = &element_info[element];
2631
2632   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2633     ei->description[i] = getFile8Bit(file);
2634   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2635
2636   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2637
2638   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
2639
2640   ei->num_change_pages = getFile8Bit(file);
2641
2642   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2643   if (chunk_size_expected != chunk_size)
2644   {
2645     ReadUnusedBytesFromFile(file, chunk_size - 43);
2646     return chunk_size_expected;
2647   }
2648
2649   ei->ce_value_fixed_initial = getFile16BitBE(file);
2650   ei->ce_value_random_initial = getFile16BitBE(file);
2651   ei->use_last_ce_value = getFile8Bit(file);
2652
2653   ei->use_gfx_element = getFile8Bit(file);
2654   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2655
2656   ei->collect_score_initial = getFile8Bit(file);
2657   ei->collect_count_initial = getFile8Bit(file);
2658
2659   ei->drop_delay_fixed = getFile8Bit(file);
2660   ei->push_delay_fixed = getFile8Bit(file);
2661   ei->drop_delay_random = getFile8Bit(file);
2662   ei->push_delay_random = getFile8Bit(file);
2663   ei->move_delay_fixed = getFile16BitBE(file);
2664   ei->move_delay_random = getFile16BitBE(file);
2665
2666   /* bits 0 - 15 of "move_pattern" ... */
2667   ei->move_pattern = getFile16BitBE(file);
2668   ei->move_direction_initial = getFile8Bit(file);
2669   ei->move_stepsize = getFile8Bit(file);
2670
2671   ei->slippery_type = getFile8Bit(file);
2672
2673   for (y = 0; y < 3; y++)
2674     for (x = 0; x < 3; x++)
2675       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2676
2677   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2678   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2679   ei->move_leave_type = getFile8Bit(file);
2680
2681   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2682   ei->move_pattern |= (getFile16BitBE(file) << 16);
2683
2684   ei->access_direction = getFile8Bit(file);
2685
2686   ei->explosion_delay = getFile8Bit(file);
2687   ei->ignition_delay = getFile8Bit(file);
2688   ei->explosion_type = getFile8Bit(file);
2689
2690   /* some free bytes for future custom property values and padding */
2691   ReadUnusedBytesFromFile(file, 1);
2692
2693   /* ---------- change page property values (48 bytes) --------------------- */
2694
2695   setElementChangePages(ei, ei->num_change_pages);
2696
2697   for (i = 0; i < ei->num_change_pages; i++)
2698   {
2699     struct ElementChangeInfo *change = &ei->change_page[i];
2700     unsigned int event_bits;
2701
2702     /* always start with reliable default values */
2703     setElementChangeInfoToDefaults(change);
2704
2705     /* bits 0 - 31 of "has_event[]" ... */
2706     event_bits = getFile32BitBE(file);
2707     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2708       if (event_bits & (1 << j))
2709         change->has_event[j] = TRUE;
2710
2711     change->target_element = getMappedElement(getFile16BitBE(file));
2712
2713     change->delay_fixed = getFile16BitBE(file);
2714     change->delay_random = getFile16BitBE(file);
2715     change->delay_frames = getFile16BitBE(file);
2716
2717     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2718
2719     change->explode = getFile8Bit(file);
2720     change->use_target_content = getFile8Bit(file);
2721     change->only_if_complete = getFile8Bit(file);
2722     change->use_random_replace = getFile8Bit(file);
2723
2724     change->random_percentage = getFile8Bit(file);
2725     change->replace_when = getFile8Bit(file);
2726
2727     for (y = 0; y < 3; y++)
2728       for (x = 0; x < 3; x++)
2729         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2730
2731     change->can_change = getFile8Bit(file);
2732
2733     change->trigger_side = getFile8Bit(file);
2734
2735     change->trigger_player = getFile8Bit(file);
2736     change->trigger_page = getFile8Bit(file);
2737
2738     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2739                             CH_PAGE_ANY : (1 << change->trigger_page));
2740
2741     change->has_action = getFile8Bit(file);
2742     change->action_type = getFile8Bit(file);
2743     change->action_mode = getFile8Bit(file);
2744     change->action_arg = getFile16BitBE(file);
2745
2746     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2747     event_bits = getFile8Bit(file);
2748     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2749       if (event_bits & (1 << (j - 32)))
2750         change->has_event[j] = TRUE;
2751   }
2752
2753   /* mark this custom element as modified */
2754   ei->modified_settings = TRUE;
2755
2756   return chunk_size;
2757 }
2758
2759 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
2760 {
2761   struct ElementInfo *ei;
2762   struct ElementGroupInfo *group;
2763   int element;
2764   int i;
2765
2766   element = getMappedElement(getFile16BitBE(file));
2767
2768   if (!IS_GROUP_ELEMENT(element))
2769   {
2770     Error(ERR_WARN, "invalid group element number %d", element);
2771
2772     ReadUnusedBytesFromFile(file, chunk_size - 2);
2773     return chunk_size;
2774   }
2775
2776   ei = &element_info[element];
2777
2778   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2779     ei->description[i] = getFile8Bit(file);
2780   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2781
2782   group = element_info[element].group;
2783
2784   group->num_elements = getFile8Bit(file);
2785
2786   ei->use_gfx_element = getFile8Bit(file);
2787   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2788
2789   group->choice_mode = getFile8Bit(file);
2790
2791   /* some free bytes for future values and padding */
2792   ReadUnusedBytesFromFile(file, 3);
2793
2794   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2795     group->element[i] = getMappedElement(getFile16BitBE(file));
2796
2797   /* mark this group element as modified */
2798   element_info[element].modified_settings = TRUE;
2799
2800   return chunk_size;
2801 }
2802
2803 static int LoadLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *conf,
2804                                 int element, int real_element)
2805 {
2806   int micro_chunk_size = 0;
2807   int conf_type = getFile8Bit(file);
2808   int byte_mask = conf_type & CONF_MASK_BYTES;
2809   boolean element_found = FALSE;
2810   int i;
2811
2812   micro_chunk_size += 1;
2813
2814   if (byte_mask == CONF_MASK_MULTI_BYTES)
2815   {
2816     int num_bytes = getFile16BitBE(file);
2817     byte *buffer = checked_malloc(num_bytes);
2818
2819     ReadBytesFromFile(file, buffer, num_bytes);
2820
2821     for (i = 0; conf[i].data_type != -1; i++)
2822     {
2823       if (conf[i].element == element &&
2824           conf[i].conf_type == conf_type)
2825       {
2826         int data_type = conf[i].data_type;
2827         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2828         int max_num_entities = conf[i].max_num_entities;
2829
2830         if (num_entities > max_num_entities)
2831         {
2832           Error(ERR_WARN,
2833                 "truncating number of entities for element %d from %d to %d",
2834                 element, num_entities, max_num_entities);
2835
2836           num_entities = max_num_entities;
2837         }
2838
2839         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2840                                   data_type == TYPE_CONTENT_LIST))
2841         {
2842           /* for element and content lists, zero entities are not allowed */
2843           Error(ERR_WARN, "found empty list of entities for element %d",
2844                 element);
2845
2846           /* do not set "num_entities" here to prevent reading behind buffer */
2847
2848           *(int *)(conf[i].num_entities) = 1;   /* at least one is required */
2849         }
2850         else
2851         {
2852           *(int *)(conf[i].num_entities) = num_entities;
2853         }
2854
2855         element_found = TRUE;
2856
2857         if (data_type == TYPE_STRING)
2858         {
2859           char *string = (char *)(conf[i].value);
2860           int j;
2861
2862           for (j = 0; j < max_num_entities; j++)
2863             string[j] = (j < num_entities ? buffer[j] : '\0');
2864         }
2865         else if (data_type == TYPE_ELEMENT_LIST)
2866         {
2867           int *element_array = (int *)(conf[i].value);
2868           int j;
2869
2870           for (j = 0; j < num_entities; j++)
2871             element_array[j] =
2872               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2873         }
2874         else if (data_type == TYPE_CONTENT_LIST)
2875         {
2876           struct Content *content= (struct Content *)(conf[i].value);
2877           int c, x, y;
2878
2879           for (c = 0; c < num_entities; c++)
2880             for (y = 0; y < 3; y++)
2881               for (x = 0; x < 3; x++)
2882                 content[c].e[x][y] =
2883                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2884         }
2885         else
2886           element_found = FALSE;
2887
2888         break;
2889       }
2890     }
2891
2892     checked_free(buffer);
2893
2894     micro_chunk_size += 2 + num_bytes;
2895   }
2896   else          /* constant size configuration data (1, 2 or 4 bytes) */
2897   {
2898     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
2899                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2900                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2901
2902     for (i = 0; conf[i].data_type != -1; i++)
2903     {
2904       if (conf[i].element == element &&
2905           conf[i].conf_type == conf_type)
2906       {
2907         int data_type = conf[i].data_type;
2908
2909         if (data_type == TYPE_ELEMENT)
2910           value = getMappedElement(value);
2911
2912         if (data_type == TYPE_BOOLEAN)
2913           *(boolean *)(conf[i].value) = value;
2914         else
2915           *(int *)    (conf[i].value) = value;
2916
2917         element_found = TRUE;
2918
2919         break;
2920       }
2921     }
2922
2923     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2924   }
2925
2926   if (!element_found)
2927   {
2928     char *error_conf_chunk_bytes =
2929       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2930        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2931        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2932     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2933     int error_element = real_element;
2934
2935     Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2936           error_conf_chunk_bytes, error_conf_chunk_token,
2937           error_element, EL_NAME(error_element));
2938   }
2939
2940   return micro_chunk_size;
2941 }
2942
2943 static int LoadLevel_INFO(FILE *file, int chunk_size, struct LevelInfo *level)
2944 {
2945   int real_chunk_size = 0;
2946
2947   li = *level;          /* copy level data into temporary buffer */
2948
2949   while (!feof(file))
2950   {
2951     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
2952
2953     if (real_chunk_size >= chunk_size)
2954       break;
2955   }
2956
2957   *level = li;          /* copy temporary buffer back to level data */
2958
2959   return real_chunk_size;
2960 }
2961
2962 static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
2963 {
2964   int real_chunk_size = 0;
2965
2966   li = *level;          /* copy level data into temporary buffer */
2967
2968   while (!feof(file))
2969   {
2970     int element = getMappedElement(getFile16BitBE(file));
2971
2972     real_chunk_size += 2;
2973     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
2974                                             element, element);
2975     if (real_chunk_size >= chunk_size)
2976       break;
2977   }
2978
2979   *level = li;          /* copy temporary buffer back to level data */
2980
2981   return real_chunk_size;
2982 }
2983
2984 static int LoadLevel_ELEM(FILE *file, int chunk_size, struct LevelInfo *level)
2985 {
2986   int real_chunk_size = 0;
2987
2988   li = *level;          /* copy level data into temporary buffer */
2989
2990   while (!feof(file))
2991   {
2992     int element = getMappedElement(getFile16BitBE(file));
2993
2994     real_chunk_size += 2;
2995     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
2996                                             element, element);
2997     if (real_chunk_size >= chunk_size)
2998       break;
2999   }
3000
3001   *level = li;          /* copy temporary buffer back to level data */
3002
3003   return real_chunk_size;
3004 }
3005
3006 static int LoadLevel_NOTE(FILE *file, int chunk_size, struct LevelInfo *level)
3007 {
3008   int element = getMappedElement(getFile16BitBE(file));
3009   int envelope_nr = element - EL_ENVELOPE_1;
3010   int real_chunk_size = 2;
3011
3012   while (!feof(file))
3013   {
3014     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3015                                             -1, element);
3016
3017     if (real_chunk_size >= chunk_size)
3018       break;
3019   }
3020
3021   level->envelope[envelope_nr] = xx_envelope;
3022
3023   return real_chunk_size;
3024 }
3025
3026 static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
3027 {
3028   int element = getMappedElement(getFile16BitBE(file));
3029   int real_chunk_size = 2;
3030   struct ElementInfo *ei = &element_info[element];
3031   int i;
3032
3033   xx_ei = *ei;          /* copy element data into temporary buffer */
3034
3035   xx_ei.num_change_pages = -1;
3036
3037   while (!feof(file))
3038   {
3039     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3040                                             -1, element);
3041     if (xx_ei.num_change_pages != -1)
3042       break;
3043
3044     if (real_chunk_size >= chunk_size)
3045       break;
3046   }
3047
3048   *ei = xx_ei;
3049
3050   if (ei->num_change_pages == -1)
3051   {
3052     Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3053           EL_NAME(element));
3054
3055     ei->num_change_pages = 1;
3056
3057     setElementChangePages(ei, 1);
3058     setElementChangeInfoToDefaults(ei->change);
3059
3060     return real_chunk_size;
3061   }
3062
3063   /* initialize number of change pages stored for this custom element */
3064   setElementChangePages(ei, ei->num_change_pages);
3065   for (i = 0; i < ei->num_change_pages; i++)
3066     setElementChangeInfoToDefaults(&ei->change_page[i]);
3067
3068   /* start with reading properties for the first change page */
3069   xx_current_change_page = 0;
3070
3071   while (!feof(file))
3072   {
3073     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3074
3075     xx_change = *change;        /* copy change data into temporary buffer */
3076
3077     resetEventBits();           /* reset bits; change page might have changed */
3078
3079     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3080                                             -1, element);
3081
3082     *change = xx_change;
3083
3084     setEventFlagsFromEventBits(change);
3085
3086     if (real_chunk_size >= chunk_size)
3087       break;
3088   }
3089
3090   return real_chunk_size;
3091 }
3092
3093 static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
3094 {
3095   int element = getMappedElement(getFile16BitBE(file));
3096   int real_chunk_size = 2;
3097   struct ElementInfo *ei = &element_info[element];
3098   struct ElementGroupInfo *group = ei->group;
3099
3100   xx_ei = *ei;          /* copy element data into temporary buffer */
3101   xx_group = *group;    /* copy group data into temporary buffer */
3102
3103   while (!feof(file))
3104   {
3105     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3106                                             -1, element);
3107
3108     if (real_chunk_size >= chunk_size)
3109       break;
3110   }
3111
3112   *ei = xx_ei;
3113   *group = xx_group;
3114
3115   return real_chunk_size;
3116 }
3117
3118 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3119                                       struct LevelFileInfo *level_file_info)
3120 {
3121   char *filename = level_file_info->filename;
3122   char cookie[MAX_LINE_LEN];
3123   char chunk_name[CHUNK_ID_LEN + 1];
3124   int chunk_size;
3125   FILE *file;
3126
3127   if (!(file = fopen(filename, MODE_READ)))
3128   {
3129     level->no_valid_file = TRUE;
3130
3131 #if 1
3132     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3133 #else
3134     if (level != &level_template)
3135       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3136 #endif
3137
3138     return;
3139   }
3140
3141   getFileChunkBE(file, chunk_name, NULL);
3142   if (strEqual(chunk_name, "RND1"))
3143   {
3144     getFile32BitBE(file);               /* not used */
3145
3146     getFileChunkBE(file, chunk_name, NULL);
3147     if (!strEqual(chunk_name, "CAVE"))
3148     {
3149       level->no_valid_file = TRUE;
3150
3151       Error(ERR_WARN, "unknown format of level file '%s'", filename);
3152       fclose(file);
3153       return;
3154     }
3155   }
3156   else  /* check for pre-2.0 file format with cookie string */
3157   {
3158     strcpy(cookie, chunk_name);
3159     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3160     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3161       cookie[strlen(cookie) - 1] = '\0';
3162
3163     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3164     {
3165       level->no_valid_file = TRUE;
3166
3167       Error(ERR_WARN, "unknown format of level file '%s'", filename);
3168       fclose(file);
3169       return;
3170     }
3171
3172     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3173     {
3174       level->no_valid_file = TRUE;
3175
3176       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3177       fclose(file);
3178       return;
3179     }
3180
3181     /* pre-2.0 level files have no game version, so use file version here */
3182     level->game_version = level->file_version;
3183   }
3184
3185   if (level->file_version < FILE_VERSION_1_2)
3186   {
3187     /* level files from versions before 1.2.0 without chunk structure */
3188     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3189     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3190   }
3191   else
3192   {
3193     static struct
3194     {
3195       char *name;
3196       int size;
3197       int (*loader)(FILE *, int, struct LevelInfo *);
3198     }
3199     chunk_info[] =
3200     {
3201       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3202       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3203       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3204       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3205       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3206       { "INFO", -1,                     LoadLevel_INFO },
3207       { "BODY", -1,                     LoadLevel_BODY },
3208       { "CONT", -1,                     LoadLevel_CONT },
3209       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3210       { "CNT3", -1,                     LoadLevel_CNT3 },
3211       { "CUS1", -1,                     LoadLevel_CUS1 },
3212       { "CUS2", -1,                     LoadLevel_CUS2 },
3213       { "CUS3", -1,                     LoadLevel_CUS3 },
3214       { "CUS4", -1,                     LoadLevel_CUS4 },
3215       { "GRP1", -1,                     LoadLevel_GRP1 },
3216       { "CONF", -1,                     LoadLevel_CONF },
3217       { "ELEM", -1,                     LoadLevel_ELEM },
3218       { "NOTE", -1,                     LoadLevel_NOTE },
3219       { "CUSX", -1,                     LoadLevel_CUSX },
3220       { "GRPX", -1,                     LoadLevel_GRPX },
3221
3222       {  NULL,  0,                      NULL }
3223     };
3224
3225     while (getFileChunkBE(file, chunk_name, &chunk_size))
3226     {
3227       int i = 0;
3228
3229       while (chunk_info[i].name != NULL &&
3230              !strEqual(chunk_name, chunk_info[i].name))
3231         i++;
3232
3233       if (chunk_info[i].name == NULL)
3234       {
3235         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3236               chunk_name, filename);
3237         ReadUnusedBytesFromFile(file, chunk_size);
3238       }
3239       else if (chunk_info[i].size != -1 &&
3240                chunk_info[i].size != chunk_size)
3241       {
3242         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3243               chunk_size, chunk_name, filename);
3244         ReadUnusedBytesFromFile(file, chunk_size);
3245       }
3246       else
3247       {
3248         /* call function to load this level chunk */
3249         int chunk_size_expected =
3250           (chunk_info[i].loader)(file, chunk_size, level);
3251
3252         /* the size of some chunks cannot be checked before reading other
3253            chunks first (like "HEAD" and "BODY") that contain some header
3254            information, so check them here */
3255         if (chunk_size_expected != chunk_size)
3256         {
3257           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3258                 chunk_size, chunk_name, filename);
3259         }
3260       }
3261     }
3262   }
3263
3264   fclose(file);
3265 }
3266
3267 /* ------------------------------------------------------------------------- */
3268 /* functions for loading EM level                                            */
3269 /* ------------------------------------------------------------------------- */
3270
3271 #if 0
3272
3273 static int map_em_element_yam(int element)
3274 {
3275   switch (element)
3276   {
3277     case 0x00:  return EL_EMPTY;
3278     case 0x01:  return EL_EMERALD;
3279     case 0x02:  return EL_DIAMOND;
3280     case 0x03:  return EL_ROCK;
3281     case 0x04:  return EL_ROBOT;
3282     case 0x05:  return EL_SPACESHIP_UP;
3283     case 0x06:  return EL_BOMB;
3284     case 0x07:  return EL_BUG_UP;
3285     case 0x08:  return EL_AMOEBA_DROP;
3286     case 0x09:  return EL_NUT;
3287     case 0x0a:  return EL_YAMYAM;
3288     case 0x0b:  return EL_QUICKSAND_FULL;
3289     case 0x0c:  return EL_SAND;
3290     case 0x0d:  return EL_WALL_SLIPPERY;
3291     case 0x0e:  return EL_STEELWALL;
3292     case 0x0f:  return EL_WALL;
3293     case 0x10:  return EL_EM_KEY_1;
3294     case 0x11:  return EL_EM_KEY_2;
3295     case 0x12:  return EL_EM_KEY_4;
3296     case 0x13:  return EL_EM_KEY_3;
3297     case 0x14:  return EL_MAGIC_WALL;
3298     case 0x15:  return EL_ROBOT_WHEEL;
3299     case 0x16:  return EL_DYNAMITE;
3300
3301     case 0x17:  return EL_EM_KEY_1;                     /* EMC */
3302     case 0x18:  return EL_BUG_UP;                       /* EMC */
3303     case 0x1a:  return EL_DIAMOND;                      /* EMC */
3304     case 0x1b:  return EL_EMERALD;                      /* EMC */
3305     case 0x25:  return EL_NUT;                          /* EMC */
3306     case 0x80:  return EL_EMPTY;                        /* EMC */
3307     case 0x85:  return EL_EM_KEY_1;                     /* EMC */
3308     case 0x86:  return EL_EM_KEY_2;                     /* EMC */
3309     case 0x87:  return EL_EM_KEY_4;                     /* EMC */
3310     case 0x88:  return EL_EM_KEY_3;                     /* EMC */
3311     case 0x94:  return EL_QUICKSAND_EMPTY;              /* EMC */
3312     case 0x9a:  return EL_AMOEBA_WET;                   /* EMC */
3313     case 0xaf:  return EL_DYNAMITE;                     /* EMC */
3314     case 0xbd:  return EL_SAND;                         /* EMC */
3315
3316     default:
3317       Error(ERR_WARN, "invalid level element %d", element);
3318       return EL_UNKNOWN;
3319   }
3320 }
3321
3322 static int map_em_element_field(int element)
3323 {
3324   if (element >= 0xc8 && element <= 0xe1)
3325     return EL_CHAR_A + (element - 0xc8);
3326   else if (element >= 0xe2 && element <= 0xeb)
3327     return EL_CHAR_0 + (element - 0xe2);
3328
3329   switch (element)
3330   {
3331     case 0x00:  return EL_ROCK;
3332     case 0x01:  return EL_ROCK;                         /* EMC */
3333     case 0x02:  return EL_DIAMOND;
3334     case 0x03:  return EL_DIAMOND;
3335     case 0x04:  return EL_ROBOT;
3336     case 0x05:  return EL_ROBOT;                        /* EMC */
3337     case 0x06:  return EL_EMPTY_SPACE;                  /* EMC */
3338     case 0x07:  return EL_EMPTY_SPACE;                  /* EMC */
3339     case 0x08:  return EL_SPACESHIP_UP;
3340     case 0x09:  return EL_SPACESHIP_RIGHT;
3341     case 0x0a:  return EL_SPACESHIP_DOWN;
3342     case 0x0b:  return EL_SPACESHIP_LEFT;
3343     case 0x0c:  return EL_SPACESHIP_UP;
3344     case 0x0d:  return EL_SPACESHIP_RIGHT;
3345     case 0x0e:  return EL_SPACESHIP_DOWN;
3346     case 0x0f:  return EL_SPACESHIP_LEFT;
3347
3348     case 0x10:  return EL_BOMB;
3349     case 0x11:  return EL_BOMB;                         /* EMC */
3350     case 0x12:  return EL_EMERALD;
3351     case 0x13:  return EL_EMERALD;
3352     case 0x14:  return EL_BUG_UP;
3353     case 0x15:  return EL_BUG_RIGHT;
3354     case 0x16:  return EL_BUG_DOWN;
3355     case 0x17:  return EL_BUG_LEFT;
3356     case 0x18:  return EL_BUG_UP;
3357     case 0x19:  return EL_BUG_RIGHT;
3358     case 0x1a:  return EL_BUG_DOWN;
3359     case 0x1b:  return EL_BUG_LEFT;
3360     case 0x1c:  return EL_AMOEBA_DROP;
3361     case 0x1d:  return EL_AMOEBA_DROP;                  /* EMC */
3362     case 0x1e:  return EL_AMOEBA_DROP;                  /* EMC */
3363     case 0x1f:  return EL_AMOEBA_DROP;                  /* EMC */
3364
3365     case 0x20:  return EL_ROCK;
3366     case 0x21:  return EL_BOMB;                         /* EMC */
3367     case 0x22:  return EL_DIAMOND;                      /* EMC */
3368     case 0x23:  return EL_EMERALD;                      /* EMC */
3369     case 0x24:  return EL_MAGIC_WALL;
3370     case 0x25:  return EL_NUT;
3371     case 0x26:  return EL_NUT;                          /* EMC */
3372     case 0x27:  return EL_NUT;                          /* EMC */
3373
3374       /* looks like magic wheel, but is _always_ activated */
3375     case 0x28:  return EL_ROBOT_WHEEL;                  /* EMC */
3376
3377     case 0x29:  return EL_YAMYAM;       /* up */
3378     case 0x2a:  return EL_YAMYAM;       /* down */
3379     case 0x2b:  return EL_YAMYAM;       /* left */      /* EMC */
3380     case 0x2c:  return EL_YAMYAM;       /* right */     /* EMC */
3381     case 0x2d:  return EL_QUICKSAND_FULL;
3382     case 0x2e:  return EL_EMPTY_SPACE;                  /* EMC */
3383     case 0x2f:  return EL_EMPTY_SPACE;                  /* EMC */
3384
3385     case 0x30:  return EL_EMPTY_SPACE;                  /* EMC */
3386     case 0x31:  return EL_SAND;                         /* EMC */
3387     case 0x32:  return EL_SAND;                         /* EMC */
3388     case 0x33:  return EL_SAND;                         /* EMC */
3389     case 0x34:  return EL_QUICKSAND_FULL;               /* EMC */
3390     case 0x35:  return EL_QUICKSAND_FULL;               /* EMC */
3391     case 0x36:  return EL_QUICKSAND_FULL;               /* EMC */
3392     case 0x37:  return EL_SAND;                         /* EMC */
3393     case 0x38:  return EL_ROCK;                         /* EMC */
3394     case 0x39:  return EL_EXPANDABLE_WALL_HORIZONTAL;   /* EMC */
3395     case 0x3a:  return EL_EXPANDABLE_WALL_VERTICAL;     /* EMC */
3396     case 0x3b:  return EL_DYNAMITE_ACTIVE;      /* 1 */
3397     case 0x3c:  return EL_DYNAMITE_ACTIVE;      /* 2 */
3398     case 0x3d:  return EL_DYNAMITE_ACTIVE;      /* 3 */
3399     case 0x3e:  return EL_DYNAMITE_ACTIVE;      /* 4 */
3400     case 0x3f:  return EL_ACID_POOL_BOTTOM;
3401
3402     case 0x40:  return EL_EXIT_OPEN;    /* 1 */
3403     case 0x41:  return EL_EXIT_OPEN;    /* 2 */
3404     case 0x42:  return EL_EXIT_OPEN;    /* 3 */
3405     case 0x43:  return EL_BALLOON;                      /* EMC */
3406     case 0x44:  return EL_UNKNOWN;                      /* EMC ("plant") */
3407     case 0x45:  return EL_SPRING;                       /* EMC */
3408     case 0x46:  return EL_SPRING;       /* falling */   /* EMC */
3409     case 0x47:  return EL_SPRING;       /* left */      /* EMC */
3410     case 0x48:  return EL_SPRING;       /* right */     /* EMC */
3411     case 0x49:  return EL_UNKNOWN;                      /* EMC ("ball 1") */
3412     case 0x4a:  return EL_UNKNOWN;                      /* EMC ("ball 2") */
3413     case 0x4b:  return EL_UNKNOWN;                      /* EMC ("android") */
3414     case 0x4c:  return EL_EMPTY_SPACE;                  /* EMC */
3415     case 0x4d:  return EL_UNKNOWN;                      /* EMC ("android") */
3416     case 0x4e:  return EL_INVISIBLE_WALL;               /* EMC (? "android") */
3417     case 0x4f:  return EL_UNKNOWN;                      /* EMC ("android") */
3418
3419     case 0x50:  return EL_UNKNOWN;                      /* EMC ("android") */
3420     case 0x51:  return EL_UNKNOWN;                      /* EMC ("android") */
3421     case 0x52:  return EL_UNKNOWN;                      /* EMC ("android") */
3422     case 0x53:  return EL_UNKNOWN;                      /* EMC ("android") */
3423     case 0x54:  return EL_UNKNOWN;                      /* EMC ("android") */
3424     case 0x55:  return EL_EMPTY_SPACE;                  /* EMC */
3425     case 0x56:  return EL_EMPTY_SPACE;                  /* EMC */
3426     case 0x57:  return EL_EMPTY_SPACE;                  /* EMC */
3427     case 0x58:  return EL_EMPTY_SPACE;                  /* EMC */
3428     case 0x59:  return EL_EMPTY_SPACE;                  /* EMC */
3429     case 0x5a:  return EL_EMPTY_SPACE;                  /* EMC */
3430     case 0x5b:  return EL_EMPTY_SPACE;                  /* EMC */
3431     case 0x5c:  return EL_EMPTY_SPACE;                  /* EMC */
3432     case 0x5d:  return EL_EMPTY_SPACE;                  /* EMC */
3433     case 0x5e:  return EL_EMPTY_SPACE;                  /* EMC */
3434     case 0x5f:  return EL_EMPTY_SPACE;                  /* EMC */
3435
3436     case 0x60:  return EL_EMPTY_SPACE;                  /* EMC */
3437     case 0x61:  return EL_EMPTY_SPACE;                  /* EMC */
3438     case 0x62:  return EL_EMPTY_SPACE;                  /* EMC */
3439     case 0x63:  return EL_SPRING;       /* left */      /* EMC */
3440     case 0x64:  return EL_SPRING;       /* right */     /* EMC */
3441     case 0x65:  return EL_ACID;         /* 1 */         /* EMC */
3442     case 0x66:  return EL_ACID;         /* 2 */         /* EMC */
3443     case 0x67:  return EL_ACID;         /* 3 */         /* EMC */
3444     case 0x68:  return EL_ACID;         /* 4 */         /* EMC */
3445     case 0x69:  return EL_ACID;         /* 5 */         /* EMC */
3446     case 0x6a:  return EL_ACID;         /* 6 */         /* EMC */
3447     case 0x6b:  return EL_ACID;         /* 7 */         /* EMC */
3448     case 0x6c:  return EL_ACID;         /* 8 */         /* EMC */
3449     case 0x6d:  return EL_EMPTY_SPACE;                  /* EMC */
3450     case 0x6e:  return EL_EMPTY_SPACE;                  /* EMC */
3451     case 0x6f:  return EL_EMPTY_SPACE;                  /* EMC */
3452
3453     case 0x70:  return EL_EMPTY_SPACE;                  /* EMC */
3454     case 0x71:  return EL_EMPTY_SPACE;                  /* EMC */
3455     case 0x72:  return EL_NUT;          /* left */      /* EMC */
3456     case 0x73:  return EL_SAND;                         /* EMC (? "nut") */
3457     case 0x74:  return EL_STEELWALL;
3458     case 0x75:  return EL_EMPTY_SPACE;                  /* EMC */
3459     case 0x76:  return EL_EMPTY_SPACE;                  /* EMC */
3460     case 0x77:  return EL_BOMB;         /* left */      /* EMC */
3461     case 0x78:  return EL_BOMB;         /* right */     /* EMC */
3462     case 0x79:  return EL_ROCK;         /* left */      /* EMC */
3463     case 0x7a:  return EL_ROCK;         /* right */     /* EMC */
3464     case 0x7b:  return EL_ACID;                         /* (? EMC "blank") */
3465     case 0x7c:  return EL_EMPTY_SPACE;                  /* EMC */
3466     case 0x7d:  return EL_EMPTY_SPACE;                  /* EMC */
3467     case 0x7e:  return EL_EMPTY_SPACE;                  /* EMC */
3468     case 0x7f:  return EL_EMPTY_SPACE;                  /* EMC */
3469
3470     case 0x80:  return EL_EMPTY;
3471     case 0x81:  return EL_WALL_SLIPPERY;
3472     case 0x82:  return EL_SAND;
3473     case 0x83:  return EL_STEELWALL;
3474     case 0x84:  return EL_WALL;
3475     case 0x85:  return EL_EM_KEY_1;
3476     case 0x86:  return EL_EM_KEY_2;
3477     case 0x87:  return EL_EM_KEY_4;
3478     case 0x88:  return EL_EM_KEY_3;
3479     case 0x89:  return EL_EM_GATE_1;
3480     case 0x8a:  return EL_EM_GATE_2;
3481     case 0x8b:  return EL_EM_GATE_4;
3482     case 0x8c:  return EL_EM_GATE_3;
3483     case 0x8d:  return EL_INVISIBLE_WALL;               /* EMC (? "dripper") */
3484     case 0x8e:  return EL_EM_GATE_1_GRAY;
3485     case 0x8f:  return EL_EM_GATE_2_GRAY;
3486
3487     case 0x90:  return EL_EM_GATE_4_GRAY;
3488     case 0x91:  return EL_EM_GATE_3_GRAY;
3489     case 0x92:  return EL_MAGIC_WALL;
3490     case 0x93:  return EL_ROBOT_WHEEL;
3491     case 0x94:  return EL_QUICKSAND_EMPTY;              /* (? EMC "sand") */
3492     case 0x95:  return EL_ACID_POOL_TOPLEFT;
3493     case 0x96:  return EL_ACID_POOL_TOPRIGHT;
3494     case 0x97:  return EL_ACID_POOL_BOTTOMLEFT;
3495     case 0x98:  return EL_ACID_POOL_BOTTOMRIGHT;
3496     case 0x99:  return EL_ACID;                 /* (? EMC "fake blank") */
3497     case 0x9a:  return EL_AMOEBA_DEAD;          /* 1 */
3498     case 0x9b:  return EL_AMOEBA_DEAD;          /* 2 */
3499     case 0x9c:  return EL_AMOEBA_DEAD;          /* 3 */
3500     case 0x9d:  return EL_AMOEBA_DEAD;          /* 4 */
3501     case 0x9e:  return EL_EXIT_CLOSED;
3502     case 0x9f:  return EL_CHAR_LESS;            /* arrow left */
3503
3504       /* looks like normal sand, but behaves like wall */
3505     case 0xa0:  return EL_UNKNOWN;              /* EMC ("fake grass") */
3506     case 0xa1:  return EL_UNKNOWN;              /* EMC ("lenses") */
3507     case 0xa2:  return EL_UNKNOWN;              /* EMC ("magnify") */
3508     case 0xa3:  return EL_UNKNOWN;              /* EMC ("fake blank") */
3509     case 0xa4:  return EL_UNKNOWN;              /* EMC ("fake grass") */
3510     case 0xa5:  return EL_UNKNOWN;              /* EMC ("switch") */
3511     case 0xa6:  return EL_UNKNOWN;              /* EMC ("switch") */
3512     case 0xa7:  return EL_EMPTY_SPACE;                  /* EMC */
3513     case 0xa8:  return EL_EMC_WALL_1;                   /* EMC ("decor 8") */
3514     case 0xa9:  return EL_EMC_WALL_2;                   /* EMC ("decor 9") */
3515     case 0xaa:  return EL_EMC_WALL_3;                   /* EMC ("decor 10") */
3516     case 0xab:  return EL_EMC_WALL_7;                   /* EMC ("decor 5") */
3517     case 0xac:  return EL_CHAR_COMMA;                   /* EMC */
3518     case 0xad:  return EL_CHAR_QUOTEDBL;                /* EMC */
3519     case 0xae:  return EL_CHAR_MINUS;                   /* EMC */
3520     case 0xaf:  return EL_DYNAMITE;
3521
3522     case 0xb0:  return EL_EMC_STEELWALL_1;              /* EMC ("steel 3") */
3523     case 0xb1:  return EL_EMC_WALL_8;                   /* EMC ("decor 6") */
3524     case 0xb2:  return EL_UNKNOWN;                      /* EMC ("decor 7") */
3525     case 0xb3:  return EL_STEELWALL;            /* 2 */ /* EMC */
3526     case 0xb4:  return EL_WALL_SLIPPERY;        /* 2 */ /* EMC */
3527     case 0xb5:  return EL_EMC_WALL_6;                   /* EMC ("decor 2") */
3528     case 0xb6:  return EL_EMC_WALL_5;                   /* EMC ("decor 4") */
3529     case 0xb7:  return EL_EMC_WALL_4;                   /* EMC ("decor 3") */
3530     case 0xb8:  return EL_BALLOON_SWITCH_ANY;           /* EMC */
3531     case 0xb9:  return EL_BALLOON_SWITCH_RIGHT;         /* EMC */
3532     case 0xba:  return EL_BALLOON_SWITCH_DOWN;          /* EMC */
3533     case 0xbb:  return EL_BALLOON_SWITCH_LEFT;          /* EMC */
3534     case 0xbc:  return EL_BALLOON_SWITCH_UP;            /* EMC */
3535     case 0xbd:  return EL_SAND;                         /* EMC ("dirt") */
3536     case 0xbe:  return EL_UNKNOWN;                      /* EMC ("plant") */
3537     case 0xbf:  return EL_UNKNOWN;                      /* EMC ("key 5") */
3538
3539     case 0xc0:  return EL_UNKNOWN;                      /* EMC ("key 6") */
3540     case 0xc1:  return EL_UNKNOWN;                      /* EMC ("key 7") */
3541     case 0xc2:  return EL_UNKNOWN;                      /* EMC ("key 8") */
3542     case 0xc3:  return EL_UNKNOWN;                      /* EMC ("door 5") */
3543     case 0xc4:  return EL_UNKNOWN;                      /* EMC ("door 6") */
3544     case 0xc5:  return EL_UNKNOWN;                      /* EMC ("door 7") */
3545     case 0xc6:  return EL_UNKNOWN;                      /* EMC ("door 8") */
3546     case 0xc7:  return EL_UNKNOWN;                      /* EMC ("bumper") */
3547
3548       /* characters: see above */
3549
3550     case 0xec:  return EL_CHAR_PERIOD;
3551     case 0xed:  return EL_CHAR_EXCLAM;
3552     case 0xee:  return EL_CHAR_COLON;
3553     case 0xef:  return EL_CHAR_QUESTION;
3554
3555     case 0xf0:  return EL_CHAR_GREATER;                 /* arrow right */
3556     case 0xf1:  return EL_CHAR_COPYRIGHT;               /* EMC: "decor 1" */
3557     case 0xf2:  return EL_UNKNOWN;              /* EMC ("fake door 5") */
3558     case 0xf3:  return EL_UNKNOWN;              /* EMC ("fake door 6") */
3559     case 0xf4:  return EL_UNKNOWN;              /* EMC ("fake door 7") */
3560     case 0xf5:  return EL_UNKNOWN;              /* EMC ("fake door 8") */
3561     case 0xf6:  return EL_EMPTY_SPACE;                  /* EMC */
3562     case 0xf7:  return EL_EMPTY_SPACE;                  /* EMC */
3563
3564     case 0xf8:  return EL_EMPTY_SPACE;                  /* EMC */
3565     case 0xf9:  return EL_EMPTY_SPACE;                  /* EMC */
3566     case 0xfa:  return EL_EMPTY_SPACE;                  /* EMC */
3567     case 0xfb:  return EL_EMPTY_SPACE;                  /* EMC */
3568     case 0xfc:  return EL_EMPTY_SPACE;                  /* EMC */
3569     case 0xfd:  return EL_EMPTY_SPACE;                  /* EMC */
3570
3571     case 0xfe:  return EL_PLAYER_1;                     /* EMC: "blank" */
3572     case 0xff:  return EL_PLAYER_2;                     /* EMC: "blank" */
3573
3574     default:
3575       /* should never happen (all 8-bit value cases should be handled) */
3576       Error(ERR_WARN, "invalid level element %d", element);
3577       return EL_UNKNOWN;
3578   }
3579 }
3580
3581 #define EM_LEVEL_SIZE                   2106
3582 #define EM_LEVEL_XSIZE                  64
3583 #define EM_LEVEL_YSIZE                  32
3584
3585 static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
3586                                          struct LevelFileInfo *level_file_info)
3587 {
3588   char *filename = level_file_info->filename;
3589   FILE *file;
3590   unsigned char leveldata[EM_LEVEL_SIZE];
3591   unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
3592   int nr = level_file_info->nr;
3593   int i, x, y;
3594
3595   if (!(file = fopen(filename, MODE_READ)))
3596   {
3597     level->no_valid_file = TRUE;
3598
3599     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3600
3601     return;
3602   }
3603
3604   for (i = 0; i < EM_LEVEL_SIZE; i++)
3605     leveldata[i] = fgetc(file);
3606
3607   fclose(file);
3608
3609   /* check if level data is crypted by testing against known starting bytes
3610      of the few existing crypted level files (from Emerald Mine 1 + 2) */
3611
3612   if ((leveldata[0] == 0xf1 ||
3613        leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
3614   {
3615     unsigned char code0 = 0x65;
3616     unsigned char code1 = 0x11;
3617
3618     if (leveldata[0] == 0xf5)   /* error in crypted Emerald Mine 2 levels */
3619       leveldata[0] = 0xf1;
3620
3621     /* decode crypted level data */
3622
3623     for (i = 0; i < EM_LEVEL_SIZE; i++)
3624     {
3625       leveldata[i] ^= code0;
3626       leveldata[i] -= code1;
3627
3628       code0 = (code0 + 7) & 0xff;
3629     }
3630   }
3631
3632   level->fieldx = EM_LEVEL_XSIZE;
3633   level->fieldy = EM_LEVEL_YSIZE;
3634
3635   level->time           = header[46] * 10;
3636   level->gems_needed    = header[47];
3637
3638   /* The original Emerald Mine levels have their level number stored
3639      at the second byte of the level file...
3640      Do not trust this information at other level files, e.g. EMC,
3641      but correct it anyway (normally the first row is completely
3642      steel wall, so the correction does not hurt anyway). */
3643
3644   if (leveldata[1] == nr)
3645     leveldata[1] = leveldata[2];        /* correct level number field */
3646
3647   sprintf(level->name, "Level %d", nr);         /* set level name */
3648
3649   level->score[SC_EMERALD]      = header[36];
3650   level->score[SC_DIAMOND]      = header[37];
3651   level->score[SC_ROBOT]        = header[38];
3652   level->score[SC_SPACESHIP]    = header[39];
3653   level->score[SC_BUG]          = header[40];
3654   level->score[SC_YAMYAM]       = header[41];
3655   level->score[SC_NUT]          = header[42];
3656   level->score[SC_DYNAMITE]     = header[43];
3657   level->score[SC_TIME_BONUS]   = header[44];
3658
3659   level->num_yamyam_contents = 4;
3660
3661   for (i = 0; i < level->num_yamyam_contents; i++)
3662     for (y = 0; y < 3; y++)
3663       for (x = 0; x < 3; x++)
3664         level->yamyam_content[i].e[x][y] =
3665           map_em_element_yam(header[i * 9 + y * 3 + x]);
3666
3667   level->amoeba_speed           = (header[52] * 256 + header[53]) % 256;
3668   level->time_magic_wall        = (header[54] * 256 + header[55]) * 16 / 100;
3669   level->time_wheel             = (header[56] * 256 + header[57]) * 16 / 100;
3670   level->amoeba_content         = EL_DIAMOND;
3671
3672   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3673   {
3674     int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
3675
3676     if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
3677       new_element = EL_AMOEBA_WET;
3678
3679     level->field[x][y] = new_element;
3680   }
3681
3682   x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
3683   y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
3684   level->field[x][y] = EL_PLAYER_1;
3685
3686   x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
3687   y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
3688   level->field[x][y] = EL_PLAYER_2;
3689 }
3690
3691 #endif
3692
3693 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3694 {
3695   static int ball_xy[8][2] =
3696   {
3697     { 0, 0 },
3698     { 1, 0 },
3699     { 2, 0 },
3700     { 0, 1 },
3701     { 2, 1 },
3702     { 0, 2 },
3703     { 1, 2 },
3704     { 2, 2 },
3705   };
3706   struct LevelInfo_EM *level_em = level->native_em_level;
3707   struct LEVEL *lev = level_em->lev;
3708   struct PLAYER **ply = level_em->ply;
3709   int i, j, x, y;
3710
3711   lev->width  = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3712   lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3713
3714   lev->time_seconds     = level->time;
3715   lev->required_initial = level->gems_needed;
3716
3717   lev->emerald_score    = level->score[SC_EMERALD];
3718   lev->diamond_score    = level->score[SC_DIAMOND];
3719   lev->alien_score      = level->score[SC_ROBOT];
3720   lev->tank_score       = level->score[SC_SPACESHIP];
3721   lev->bug_score        = level->score[SC_BUG];
3722   lev->eater_score      = level->score[SC_YAMYAM];
3723   lev->nut_score        = level->score[SC_NUT];
3724   lev->dynamite_score   = level->score[SC_DYNAMITE];
3725   lev->key_score        = level->score[SC_KEY];
3726   lev->exit_score       = level->score[SC_TIME_BONUS];
3727
3728   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3729     for (y = 0; y < 3; y++)
3730       for (x = 0; x < 3; x++)
3731         lev->eater_array[i][y * 3 + x] =
3732           map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3733
3734   lev->amoeba_time              = level->amoeba_speed;
3735   lev->wonderwall_time_initial  = level->time_magic_wall;
3736   lev->wheel_time               = level->time_wheel;
3737
3738   lev->android_move_time        = level->android_move_time;
3739   lev->android_clone_time       = level->android_clone_time;
3740   lev->ball_random              = level->ball_random;
3741   lev->ball_state_initial       = level->ball_state_initial;
3742   lev->ball_time                = level->ball_time;
3743   lev->num_ball_arrays          = level->num_ball_contents;
3744
3745   lev->lenses_score             = level->lenses_score;
3746   lev->magnify_score            = level->magnify_score;
3747   lev->slurp_score              = level->slurp_score;
3748
3749   lev->lenses_time              = level->lenses_time;
3750   lev->magnify_time             = level->magnify_time;
3751
3752   lev->wind_direction_initial =
3753     map_direction_RND_to_EM(level->wind_direction_initial);
3754   lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3755                            lev->wind_time : 0);
3756
3757   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3758     for (j = 0; j < 8; j++)
3759       lev->ball_array[i][j] =
3760         map_element_RND_to_EM(level->
3761                               ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3762
3763   map_android_clone_elements_RND_to_EM(level);
3764
3765   /* first fill the complete playfield with the default border element */
3766   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3767     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3768       level_em->cave[x][y] = ZBORDER;
3769
3770   if (BorderElement == EL_STEELWALL)
3771   {
3772     for (y = 0; y < lev->height + 2; y++)
3773       for (x = 0; x < lev->width + 2; x++)
3774         level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3775   }
3776
3777   /* then copy the real level contents from level file into the playfield */
3778   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3779   {
3780     int new_element = map_element_RND_to_EM(level->field[x][y]);
3781     int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3782     int xx = x + 1 + offset;
3783     int yy = y + 1 + offset;
3784
3785     if (level->field[x][y] == EL_AMOEBA_DEAD)
3786       new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3787
3788     level_em->cave[xx][yy] = new_element;
3789   }
3790
3791   for (i = 0; i < MAX_PLAYERS; i++)
3792   {
3793     ply[i]->x_initial = 0;
3794     ply[i]->y_initial = 0;
3795   }
3796
3797   /* initialize player positions and delete players from the playfield */
3798   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3799   {
3800     if (ELEM_IS_PLAYER(level->field[x][y]))
3801     {
3802       int player_nr = GET_PLAYER_NR(level->field[x][y]);
3803       int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3804       int xx = x + 1 + offset;
3805       int yy = y + 1 + offset;
3806
3807       ply[player_nr]->x_initial = xx;
3808       ply[player_nr]->y_initial = yy;
3809
3810       level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3811     }
3812   }
3813
3814   if (BorderElement == EL_STEELWALL)
3815   {
3816     lev->width  += 2;
3817     lev->height += 2;
3818   }
3819 }
3820
3821 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3822 {
3823   static int ball_xy[8][2] =
3824   {
3825     { 0, 0 },
3826     { 1, 0 },
3827     { 2, 0 },
3828     { 0, 1 },
3829     { 2, 1 },
3830     { 0, 2 },
3831     { 1, 2 },
3832     { 2, 2 },
3833   };
3834   struct LevelInfo_EM *level_em = level->native_em_level;
3835   struct LEVEL *lev = level_em->lev;
3836   struct PLAYER **ply = level_em->ply;
3837   int i, j, x, y;
3838
3839   level->fieldx = MIN(lev->width,  MAX_LEV_FIELDX);
3840   level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3841
3842   level->time        = lev->time_seconds;
3843   level->gems_needed = lev->required_initial;
3844
3845   sprintf(level->name, "Level %d", level->file_info.nr);
3846
3847   level->score[SC_EMERALD]      = lev->emerald_score;
3848   level->score[SC_DIAMOND]      = lev->diamond_score;
3849   level->score[SC_ROBOT]        = lev->alien_score;
3850   level->score[SC_SPACESHIP]    = lev->tank_score;
3851   level->score[SC_BUG]          = lev->bug_score;
3852   level->score[SC_YAMYAM]       = lev->eater_score;
3853   level->score[SC_NUT]          = lev->nut_score;
3854   level->score[SC_DYNAMITE]     = lev->dynamite_score;
3855   level->score[SC_KEY]          = lev->key_score;
3856   level->score[SC_TIME_BONUS]   = lev->exit_score;
3857
3858   level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3859
3860   for (i = 0; i < level->num_yamyam_contents; i++)
3861     for (y = 0; y < 3; y++)
3862       for (x = 0; x < 3; x++)
3863         level->yamyam_content[i].e[x][y] =
3864           map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3865
3866   level->amoeba_speed           = lev->amoeba_time;
3867   level->time_magic_wall        = lev->wonderwall_time_initial;
3868   level->time_wheel             = lev->wheel_time;
3869
3870   level->android_move_time      = lev->android_move_time;
3871   level->android_clone_time     = lev->android_clone_time;
3872   level->ball_random            = lev->ball_random;
3873   level->ball_state_initial     = lev->ball_state_initial;
3874   level->ball_time              = lev->ball_time;
3875   level->num_ball_contents      = lev->num_ball_arrays;
3876
3877   level->lenses_score           = lev->lenses_score;
3878   level->magnify_score          = lev->magnify_score;
3879   level->slurp_score            = lev->slurp_score;
3880
3881   level->lenses_time            = lev->lenses_time;
3882   level->magnify_time           = lev->magnify_time;
3883
3884   level->wind_direction_initial =
3885     map_direction_EM_to_RND(lev->wind_direction_initial);
3886
3887   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3888     for (j = 0; j < 8; j++)
3889       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3890         map_element_EM_to_RND(lev->ball_array[i][j]);
3891
3892   map_android_clone_elements_EM_to_RND(level);
3893
3894   /* convert the playfield (some elements need special treatment) */
3895   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3896   {
3897     int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3898
3899     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3900       new_element = EL_AMOEBA_DEAD;
3901
3902     level->field[x][y] = new_element;
3903   }
3904
3905   for (i = 0; i < MAX_PLAYERS; i++)
3906   {
3907     /* in case of all players set to the same field, use the first player */
3908     int nr = MAX_PLAYERS - i - 1;
3909     int jx = ply[nr]->x_initial - 1;
3910     int jy = ply[nr]->y_initial - 1;
3911
3912     if (jx != -1 && jy != -1)
3913       level->field[jx][jy] = EL_PLAYER_1 + nr;
3914   }
3915 }
3916
3917
3918 /* ------------------------------------------------------------------------- */
3919 /* functions for loading SP level                                            */
3920 /* ------------------------------------------------------------------------- */
3921
3922 #if 0
3923
3924 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
3925 #define SP_LEVEL_SIZE                   1536
3926 #define SP_LEVEL_XSIZE                  60
3927 #define SP_LEVEL_YSIZE                  24
3928 #define SP_LEVEL_NAME_LEN               23
3929
3930 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
3931                                        int nr)
3932 {
3933   int initial_player_gravity;
3934   int num_special_ports;
3935   int i, x, y;
3936
3937   /* for details of the Supaplex level format, see Herman Perk's Supaplex
3938      documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
3939
3940   /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
3941   for (y = 0; y < SP_LEVEL_YSIZE; y++)
3942   {
3943     for (x = 0; x < SP_LEVEL_XSIZE; x++)
3944     {
3945       int element_old = fgetc(file);
3946       int element_new;
3947
3948       if (element_old <= 0x27)
3949         element_new = getMappedElement(EL_SP_START + element_old);
3950       else if (element_old == 0x28)
3951         element_new = EL_INVISIBLE_WALL;
3952       else
3953       {
3954         Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
3955         Error(ERR_WARN, "invalid level element %d", element_old);
3956
3957         element_new = EL_UNKNOWN;
3958       }
3959
3960       level->field[x][y] = element_new;
3961     }
3962   }
3963
3964   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
3965
3966   /* initial gravity: 1 == "on", anything else (0) == "off" */
3967   initial_player_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
3968
3969   for (i = 0; i < MAX_PLAYERS; i++)
3970     level->initial_player_gravity[i] = initial_player_gravity;
3971
3972   ReadUnusedBytesFromFile(file, 1);     /* (not used by Supaplex engine) */
3973
3974   /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
3975   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3976     level->name[i] = fgetc(file);
3977   level->name[SP_LEVEL_NAME_LEN] = '\0';
3978
3979   /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
3980   ReadUnusedBytesFromFile(file, 1);     /* (not used by R'n'D engine) */
3981
3982   /* number of infotrons needed; 0 means that Supaplex will count the total
3983      amount of infotrons in the level and use the low byte of that number
3984      (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
3985   level->gems_needed = fgetc(file);
3986
3987   /* number of special ("gravity") port entries below (maximum 10 allowed) */
3988   num_special_ports = fgetc(file);
3989
3990   /* database of properties of up to 10 special ports (6 bytes per port) */
3991   for (i = 0; i < 10; i++)
3992   {
3993     int port_location, port_x, port_y, port_element;
3994     int gravity;
3995
3996     /* high and low byte of the location of a special port; if (x, y) are the
3997        coordinates of a port in the field and (0, 0) is the top-left corner,
3998        the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
3999        of what may be expected: Supaplex works with a game field in memory
4000        which is 2 bytes per tile) */
4001     port_location = getFile16BitBE(file);
4002
4003     /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
4004     gravity = fgetc(file);
4005
4006     /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
4007     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
4008
4009     /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
4010     ReadUnusedBytesFromFile(file, 1);   /* (not used by R'n'D engine) */
4011
4012     ReadUnusedBytesFromFile(file, 1);   /* (not used by Supaplex engine) */
4013
4014     if (i >= num_special_ports)
4015       continue;
4016
4017     port_x = (port_location / 2) % SP_LEVEL_XSIZE;
4018     port_y = (port_location / 2) / SP_LEVEL_XSIZE;
4019
4020     if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
4021         port_y < 0 || port_y >= SP_LEVEL_YSIZE)
4022     {
4023       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
4024             port_x, port_y);
4025
4026       continue;
4027     }
4028
4029     port_element = level->field[port_x][port_y];
4030
4031     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4032         port_element > EL_SP_GRAVITY_PORT_UP)
4033     {
4034       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
4035
4036       continue;
4037     }
4038
4039     /* change previous (wrong) gravity inverting special port to either
4040        gravity enabling special port or gravity disabling special port */
4041     level->field[port_x][port_y] +=
4042       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4043        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4044   }
4045
4046   ReadUnusedBytesFromFile(file, 4);     /* (not used by Supaplex engine) */
4047
4048   /* change special gravity ports without database entries to normal ports */
4049   for (y = 0; y < SP_LEVEL_YSIZE; y++)
4050     for (x = 0; x < SP_LEVEL_XSIZE; x++)
4051       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4052           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4053         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4054
4055   /* auto-determine number of infotrons if it was stored as "0" -- see above */
4056   if (level->gems_needed == 0)
4057   {
4058     for (y = 0; y < SP_LEVEL_YSIZE; y++)
4059       for (x = 0; x < SP_LEVEL_XSIZE; x++)
4060         if (level->field[x][y] == EL_SP_INFOTRON)
4061           level->gems_needed++;
4062
4063     level->gems_needed &= 0xff;         /* only use low byte -- see above */
4064   }
4065
4066   level->fieldx = SP_LEVEL_XSIZE;
4067   level->fieldy = SP_LEVEL_YSIZE;
4068
4069   level->time = 0;                      /* no time limit */
4070   level->amoeba_speed = 0;
4071   level->time_magic_wall = 0;
4072   level->time_wheel = 0;
4073   level->amoeba_content = EL_EMPTY;
4074
4075 #if 1
4076   /* original Supaplex does not use score values -- use default values */
4077 #else
4078   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4079     level->score[i] = 0;
4080 #endif
4081
4082   /* there are no yamyams in supaplex levels */
4083   for (i = 0; i < level->num_yamyam_contents; i++)
4084     for (y = 0; y < 3; y++)
4085       for (x = 0; x < 3; x++)
4086         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4087 }
4088
4089 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
4090                                      struct LevelFileInfo *level_file_info)
4091 {
4092   char *filename = level_file_info->filename;
4093   FILE *file;
4094   int nr = level_file_info->nr - leveldir_current->first_level;
4095   int i, l, x, y;
4096   char name_first, name_last;
4097   struct LevelInfo multipart_level;
4098   int multipart_xpos, multipart_ypos;
4099   boolean is_multipart_level;
4100   boolean is_first_part;
4101   boolean reading_multipart_level = FALSE;
4102   boolean use_empty_level = FALSE;
4103
4104   if (!(file = fopen(filename, MODE_READ)))
4105   {
4106     level->no_valid_file = TRUE;
4107
4108     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
4109
4110     return;
4111   }
4112
4113   /* position file stream to the requested level inside the level package */
4114   if (level_file_info->packed &&
4115       fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
4116   {
4117     level->no_valid_file = TRUE;
4118
4119     Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
4120
4121     return;
4122   }
4123
4124   /* there exist Supaplex level package files with multi-part levels which
4125      can be detected as follows: instead of leading and trailing dashes ('-')
4126      to pad the level name, they have leading and trailing numbers which are
4127      the x and y coordinations of the current part of the multi-part level;
4128      if there are '?' characters instead of numbers on the left or right side
4129      of the level name, the multi-part level consists of only horizontal or
4130      vertical parts */
4131
4132   for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
4133   {
4134     LoadLevelFromFileStream_SP(file, level, l);
4135
4136     /* check if this level is a part of a bigger multi-part level */
4137
4138     name_first = level->name[0];
4139     name_last  = level->name[SP_LEVEL_NAME_LEN - 1];
4140
4141     is_multipart_level =
4142       ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
4143        (name_last  == '?' || (name_last  >= '0' && name_last  <= '9')));
4144
4145     is_first_part =
4146       ((name_first == '?' || name_first == '1') &&
4147        (name_last  == '?' || name_last  == '1'));
4148
4149     /* correct leading multipart level meta information in level name */
4150     for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
4151       level->name[i] = '-';
4152
4153     /* correct trailing multipart level meta information in level name */
4154     for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && level->name[i] == name_last; i--)
4155       level->name[i] = '-';
4156
4157     /* ---------- check for normal single level ---------- */
4158
4159     if (!reading_multipart_level && !is_multipart_level)
4160     {
4161       /* the current level is simply a normal single-part level, and we are
4162          not reading a multi-part level yet, so return the level as it is */
4163
4164       break;
4165     }
4166
4167     /* ---------- check for empty level (unused multi-part) ---------- */
4168
4169     if (!reading_multipart_level && is_multipart_level && !is_first_part)
4170     {
4171       /* this is a part of a multi-part level, but not the first part
4172          (and we are not already reading parts of a multi-part level);
4173          in this case, use an empty level instead of the single part */
4174
4175       use_empty_level = TRUE;
4176
4177       break;
4178     }
4179
4180     /* ---------- check for finished multi-part level ---------- */
4181
4182     if (reading_multipart_level &&
4183         (!is_multipart_level ||
4184          !strEqual(level->name, multipart_level.name)))
4185     {
4186       /* we are already reading parts of a multi-part level, but this level is
4187          either not a multi-part level, or a part of a different multi-part
4188          level; in both cases, the multi-part level seems to be complete */
4189
4190       break;
4191     }
4192
4193     /* ---------- here we have one part of a multi-part level ---------- */
4194
4195     reading_multipart_level = TRUE;
4196
4197     if (is_first_part)  /* start with first part of new multi-part level */
4198     {
4199       /* copy level info structure from first part */
4200       multipart_level = *level;
4201
4202       /* clear playfield of new multi-part level */
4203       for (y = 0; y < MAX_LEV_FIELDY; y++)
4204         for (x = 0; x < MAX_LEV_FIELDX; x++)
4205           multipart_level.field[x][y] = EL_EMPTY;
4206     }
4207
4208     if (name_first == '?')
4209       name_first = '1';
4210     if (name_last == '?')
4211       name_last = '1';
4212
4213     multipart_xpos = (int)(name_first - '0');
4214     multipart_ypos = (int)(name_last  - '0');
4215
4216 #if 0
4217     printf("----------> part (%d/%d) of multi-part level '%s'\n",
4218            multipart_xpos, multipart_ypos, multipart_level.name);
4219 #endif
4220
4221     if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
4222         multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
4223     {
4224       Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
4225
4226       break;
4227     }
4228
4229     multipart_level.fieldx = MAX(multipart_level.fieldx,
4230                                  multipart_xpos * SP_LEVEL_XSIZE);
4231     multipart_level.fieldy = MAX(multipart_level.fieldy,
4232                                  multipart_ypos * SP_LEVEL_YSIZE);
4233
4234     /* copy level part at the right position of multi-part level */
4235     for (y = 0; y < SP_LEVEL_YSIZE; y++)
4236     {
4237       for (x = 0; x < SP_LEVEL_XSIZE; x++)
4238       {
4239         int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
4240         int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
4241
4242         multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
4243       }
4244     }
4245   }
4246
4247   fclose(file);
4248
4249   if (use_empty_level)
4250   {
4251     setLevelInfoToDefaults(level);
4252
4253     level->fieldx = SP_LEVEL_XSIZE;
4254     level->fieldy = SP_LEVEL_YSIZE;
4255
4256     for (y = 0; y < SP_LEVEL_YSIZE; y++)
4257       for (x = 0; x < SP_LEVEL_XSIZE; x++)
4258         level->field[x][y] = EL_EMPTY;
4259
4260     strcpy(level->name, "-------- EMPTY --------");
4261
4262     Error(ERR_WARN, "single part of multi-part level -- using empty level");
4263   }
4264
4265   if (reading_multipart_level)
4266     *level = multipart_level;
4267 }
4268
4269 #endif
4270
4271 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4272 {
4273   struct LevelInfo_SP *level_sp = level->native_sp_level;
4274   LevelInfoType *header = &level_sp->header;
4275   int i, x, y;
4276
4277   level_sp->width  = level->fieldx;
4278   level_sp->height = level->fieldy;
4279
4280   for (x = 0; x < level->fieldx; x++)
4281   {
4282     for (y = 0; y < level->fieldy; y++)
4283     {
4284       int element_old = level->field[x][y];
4285       int element_new;
4286
4287       if (element_old >= EL_SP_START &&
4288           element_old <= EL_SP_END)
4289         element_new = element_old - EL_SP_START;
4290       else if (element_old == EL_EMPTY_SPACE)
4291         element_new = 0x00;
4292       else if (element_old == EL_INVISIBLE_WALL)
4293         element_new = 0x28;
4294       else
4295         element_new = 0x20;     /* map unknown elements to yellow "hardware" */
4296
4297       level_sp->playfield[x][y] = element_new;
4298     }
4299   }
4300
4301   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4302
4303   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4304     header->LevelTitle[i] = level->name[i];
4305   /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
4306
4307   header->InfotronsNeeded = level->gems_needed;
4308
4309   header->SpecialPortCount = 0;
4310
4311   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4312   {
4313     boolean gravity_port_found = FALSE;
4314     boolean gravity_port_valid = FALSE;
4315     int gravity_port_flag;
4316     int gravity_port_base_element;
4317     int element = level->field[x][y];
4318
4319     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4320         element <= EL_SP_GRAVITY_ON_PORT_UP)
4321     {
4322       gravity_port_found = TRUE;
4323       gravity_port_valid = TRUE;
4324       gravity_port_flag = 1;
4325       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4326     }
4327     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4328              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4329     {
4330       gravity_port_found = TRUE;
4331       gravity_port_valid = TRUE;
4332       gravity_port_flag = 0;
4333       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4334     }
4335     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4336              element <= EL_SP_GRAVITY_PORT_UP)
4337     {
4338       /* change R'n'D style gravity inverting special port to normal port
4339          (there are no gravity inverting ports in native Supaplex engine) */
4340
4341       gravity_port_found = TRUE;
4342       gravity_port_valid = FALSE;
4343       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4344     }
4345
4346     if (gravity_port_found)
4347     {
4348       if (gravity_port_valid &&
4349           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4350       {
4351         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4352
4353         port->PortLocation = (y * level->fieldx + x) * 2;
4354         port->Gravity = gravity_port_flag;
4355
4356         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4357
4358         header->SpecialPortCount++;
4359       }
4360       else
4361       {
4362         /* change special gravity port to normal port */
4363
4364         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4365       }
4366
4367       level_sp->playfield[x][y] = element - EL_SP_START;
4368     }
4369   }
4370 }
4371
4372 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4373 {
4374   struct LevelInfo_SP *level_sp = level->native_sp_level;
4375   LevelInfoType *header = &level_sp->header;
4376   int i, x, y;
4377
4378   level->fieldx = level_sp->width;
4379   level->fieldy = level_sp->height;
4380
4381   for (x = 0; x < level->fieldx; x++)
4382   {
4383     for (y = 0; y < level->fieldy; y++)
4384     {
4385       int element_old = level_sp->playfield[x][y];
4386       int element_new;
4387
4388       if (element_old <= 0x27)
4389         element_new = getMappedElement(EL_SP_START + element_old);
4390       else if (element_old == 0x28)
4391         element_new = EL_INVISIBLE_WALL;
4392       else
4393       {
4394         Error(ERR_WARN, "invalid element %d at position %d, %d",
4395               element_old, x, y);
4396
4397         element_new = EL_UNKNOWN;
4398       }
4399
4400       level->field[x][y] = element_new;
4401     }
4402   }
4403
4404   for (i = 0; i < MAX_PLAYERS; i++)
4405     level->initial_player_gravity[i] =
4406       (header->InitialGravity == 1 ? TRUE : FALSE);
4407
4408   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4409     level->name[i] = header->LevelTitle[i];
4410   level->name[SP_LEVEL_NAME_LEN] = '\0';
4411
4412   level->gems_needed = header->InfotronsNeeded;
4413
4414   for (i = 0; i < header->SpecialPortCount; i++)
4415   {
4416     SpecialPortType *port = &header->SpecialPort[i];
4417     int port_location = port->PortLocation;
4418     int gravity = port->Gravity;
4419     int port_x, port_y, port_element;
4420
4421     port_x = (port_location / 2) % level->fieldx;
4422     port_y = (port_location / 2) / level->fieldx;
4423
4424     if (port_x < 0 || port_x >= level->fieldx ||
4425         port_y < 0 || port_y >= level->fieldy)
4426     {
4427       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
4428             port_x, port_y);
4429
4430       continue;
4431     }
4432
4433     port_element = level->field[port_x][port_y];
4434
4435     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4436         port_element > EL_SP_GRAVITY_PORT_UP)
4437     {
4438       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
4439
4440       continue;
4441     }
4442
4443     /* change previous (wrong) gravity inverting special port to either
4444        gravity enabling special port or gravity disabling special port */
4445     level->field[port_x][port_y] +=
4446       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4447        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4448   }
4449
4450   /* change special gravity ports without database entries to normal ports */
4451   for (x = 0; x < level->fieldx; x++)
4452     for (y = 0; y < level->fieldy; y++)
4453       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4454           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4455         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4456
4457   level->time = 0;                      /* no time limit */
4458   level->amoeba_speed = 0;
4459   level->time_magic_wall = 0;
4460   level->time_wheel = 0;
4461   level->amoeba_content = EL_EMPTY;
4462
4463 #if 1
4464   /* original Supaplex does not use score values -- use default values */
4465 #else
4466   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4467     level->score[i] = 0;
4468 #endif
4469
4470   /* there are no yamyams in supaplex levels */
4471   for (i = 0; i < level->num_yamyam_contents; i++)
4472     for (x = 0; x < 3; x++)
4473       for (y = 0; y < 3; y++)
4474         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4475 }
4476
4477 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4478 {
4479   struct LevelInfo_SP *level_sp = level->native_sp_level;
4480   struct DemoInfo_SP *demo = &level_sp->demo;
4481   int i, j;
4482
4483   /* always start with reliable default values */
4484   demo->is_available = FALSE;
4485   demo->length = 0;
4486
4487   if (TAPE_IS_EMPTY(tape))
4488     return;
4489
4490   demo->level_nr = tape.level_nr;       /* (currently not used) */
4491
4492   level_sp->header.DemoRandomSeed = tape.random_seed;
4493
4494   demo->length = 0;
4495   for (i = 0; i < tape.length; i++)
4496   {
4497     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4498     int demo_repeat = tape.pos[i].delay;
4499
4500     for (j = 0; j < demo_repeat / 16; j++)
4501       demo->data[demo->length++] = 0xf0 | demo_action;
4502
4503     if (demo_repeat % 16)
4504       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4505   }
4506
4507   demo->data[demo->length++] = 0xff;
4508
4509   demo->is_available = TRUE;
4510 }
4511
4512 static void setTapeInfoToDefaults();
4513
4514 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4515 {
4516   struct LevelInfo_SP *level_sp = level->native_sp_level;
4517   struct DemoInfo_SP *demo = &level_sp->demo;
4518   char *filename = level->file_info.filename;
4519   int i;
4520
4521   /* always start with reliable default values */
4522   setTapeInfoToDefaults();
4523
4524   if (!demo->is_available)
4525     return;
4526
4527   tape.level_nr = demo->level_nr;       /* (currently not used) */
4528   tape.length = demo->length - 1;       /* without "end of demo" byte */
4529   tape.random_seed = level_sp->header.DemoRandomSeed;
4530
4531   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4532
4533   for (i = 0; i < demo->length - 1; i++)
4534   {
4535     int demo_action = demo->data[i] & 0x0f;
4536     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4537
4538     tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
4539     tape.pos[i].delay = demo_repeat + 1;
4540   }
4541
4542   tape.length_seconds = GetTapeLength();
4543 }
4544
4545
4546 /* ------------------------------------------------------------------------- */
4547 /* functions for loading DC level                                            */
4548 /* ------------------------------------------------------------------------- */
4549
4550 #define DC_LEVEL_HEADER_SIZE            344
4551
4552 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4553 {
4554   static int last_data_encoded;
4555   static int offset1;
4556   static int offset2;
4557   int diff;
4558   int diff_hi, diff_lo;
4559   int data_hi, data_lo;
4560   unsigned short data_decoded;
4561
4562   if (init)
4563   {
4564     last_data_encoded = 0;
4565     offset1 = -1;
4566     offset2 = 0;
4567
4568     return 0;
4569   }
4570
4571   diff = data_encoded - last_data_encoded;
4572   diff_hi = diff & ~0xff;
4573   diff_lo = diff &  0xff;
4574
4575   offset2 += diff_lo;
4576
4577   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4578   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4579   data_hi = data_hi & 0xff00;
4580
4581   data_decoded = data_hi | data_lo;
4582
4583   last_data_encoded = data_encoded;
4584
4585   offset1 = (offset1 + 1) % 31;
4586   offset2 = offset2 & 0xff;
4587
4588   return data_decoded;
4589 }
4590
4591 int getMappedElement_DC(int element)
4592 {
4593   switch (element)
4594   {
4595     case 0x0000:
4596       element = EL_ROCK;
4597       break;
4598
4599       /* 0x0117 - 0x036e: (?) */
4600       /* EL_DIAMOND */
4601
4602       /* 0x042d - 0x0684: (?) */
4603       /* EL_EMERALD */
4604
4605     case 0x06f1:
4606       element = EL_NUT;
4607       break;
4608
4609     case 0x074c:
4610       element = EL_BOMB;
4611       break;
4612
4613     case 0x07a4:
4614       element = EL_PEARL;
4615       break;
4616
4617     case 0x0823:
4618       element = EL_CRYSTAL;
4619       break;
4620
4621     case 0x0e77:        /* quicksand (boulder) */
4622       element = EL_QUICKSAND_FAST_FULL;
4623       break;
4624
4625     case 0x0e99:        /* slow quicksand (boulder) */
4626       element = EL_QUICKSAND_FULL;
4627       break;
4628
4629     case 0x0ed2:
4630       element = EL_EM_EXIT_OPEN;
4631       break;
4632
4633     case 0x0ee3:
4634       element = EL_EM_EXIT_CLOSED;
4635       break;
4636
4637     case 0x0eeb:
4638       element = EL_EM_STEEL_EXIT_OPEN;
4639       break;
4640
4641     case 0x0efc:
4642       element = EL_EM_STEEL_EXIT_CLOSED;
4643       break;
4644
4645     case 0x0f4f:        /* dynamite (lit 1) */
4646       element = EL_EM_DYNAMITE_ACTIVE;
4647       break;
4648
4649     case 0x0f57:        /* dynamite (lit 2) */
4650       element = EL_EM_DYNAMITE_ACTIVE;
4651       break;
4652
4653     case 0x0f5f:        /* dynamite (lit 3) */
4654       element = EL_EM_DYNAMITE_ACTIVE;
4655       break;
4656
4657     case 0x0f67:        /* dynamite (lit 4) */
4658       element = EL_EM_DYNAMITE_ACTIVE;
4659       break;
4660
4661     case 0x0f81:
4662     case 0x0f82:
4663     case 0x0f83:
4664     case 0x0f84:
4665       element = EL_AMOEBA_WET;
4666       break;
4667
4668     case 0x0f85:
4669       element = EL_AMOEBA_DROP;
4670       break;
4671
4672     case 0x0fb9:
4673       element = EL_DC_MAGIC_WALL;
4674       break;
4675
4676     case 0x0fd0:
4677       element = EL_SPACESHIP_UP;
4678       break;
4679
4680     case 0x0fd9:
4681       element = EL_SPACESHIP_DOWN;
4682       break;
4683
4684     case 0x0ff1:
4685       element = EL_SPACESHIP_LEFT;
4686       break;
4687
4688     case 0x0ff9:
4689       element = EL_SPACESHIP_RIGHT;
4690       break;
4691
4692     case 0x1057:
4693       element = EL_BUG_UP;
4694       break;
4695
4696     case 0x1060:
4697       element = EL_BUG_DOWN;
4698       break;
4699
4700     case 0x1078:
4701       element = EL_BUG_LEFT;
4702       break;
4703
4704     case 0x1080:
4705       element = EL_BUG_RIGHT;
4706       break;
4707
4708     case 0x10de:
4709       element = EL_MOLE_UP;
4710       break;
4711
4712     case 0x10e7:
4713       element = EL_MOLE_DOWN;
4714       break;
4715
4716     case 0x10ff:
4717       element = EL_MOLE_LEFT;
4718       break;
4719
4720     case 0x1107:
4721       element = EL_MOLE_RIGHT;
4722       break;
4723
4724     case 0x11c0:
4725       element = EL_ROBOT;
4726       break;
4727
4728     case 0x13f5:
4729       element = EL_YAMYAM;
4730       break;
4731
4732     case 0x1425:
4733       element = EL_SWITCHGATE_OPEN;
4734       break;
4735
4736     case 0x1426:
4737       element = EL_SWITCHGATE_CLOSED;
4738       break;
4739
4740     case 0x1437:
4741       element = EL_DC_SWITCHGATE_SWITCH_UP;
4742       break;
4743
4744     case 0x143a:
4745       element = EL_TIMEGATE_CLOSED;
4746       break;
4747
4748     case 0x144c:        /* conveyor belt switch (green) */
4749       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4750       break;
4751
4752     case 0x144f:        /* conveyor belt switch (red) */
4753       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4754       break;
4755
4756     case 0x1452:        /* conveyor belt switch (blue) */
4757       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4758       break;
4759
4760     case 0x145b:
4761       element = EL_CONVEYOR_BELT_3_MIDDLE;
4762       break;
4763
4764     case 0x1463:
4765       element = EL_CONVEYOR_BELT_3_LEFT;
4766       break;
4767
4768     case 0x146b:
4769       element = EL_CONVEYOR_BELT_3_RIGHT;
4770       break;
4771
4772     case 0x1473:
4773       element = EL_CONVEYOR_BELT_1_MIDDLE;
4774       break;
4775
4776     case 0x147b:
4777       element = EL_CONVEYOR_BELT_1_LEFT;
4778       break;
4779
4780     case 0x1483:
4781       element = EL_CONVEYOR_BELT_1_RIGHT;
4782       break;
4783
4784     case 0x148b:
4785       element = EL_CONVEYOR_BELT_4_MIDDLE;
4786       break;
4787
4788     case 0x1493:
4789       element = EL_CONVEYOR_BELT_4_LEFT;
4790       break;
4791
4792     case 0x149b:
4793       element = EL_CONVEYOR_BELT_4_RIGHT;
4794       break;
4795
4796     case 0x14ac:
4797       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4798       break;
4799
4800     case 0x14bd:
4801       element = EL_EXPANDABLE_WALL_VERTICAL;
4802       break;
4803
4804     case 0x14c6:
4805       element = EL_EXPANDABLE_WALL_ANY;
4806       break;
4807
4808     case 0x14ce:        /* growing steel wall (left/right) */
4809       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4810       break;
4811
4812     case 0x14df:        /* growing steel wall (up/down) */
4813       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4814       break;
4815
4816     case 0x14e8:        /* growing steel wall (up/down/left/right) */
4817       element = EL_EXPANDABLE_STEELWALL_ANY;
4818       break;
4819
4820     case 0x14e9:
4821       element = EL_SHIELD_DEADLY;
4822       break;
4823
4824     case 0x1501:
4825       element = EL_EXTRA_TIME;
4826       break;
4827
4828     case 0x154f:
4829       element = EL_ACID;
4830       break;
4831
4832     case 0x1577:
4833       element = EL_EMPTY_SPACE;
4834       break;
4835
4836     case 0x1578:        /* quicksand (empty) */
4837       element = EL_QUICKSAND_FAST_EMPTY;
4838       break;
4839
4840     case 0x1579:        /* slow quicksand (empty) */
4841       element = EL_QUICKSAND_EMPTY;
4842       break;
4843
4844       /* 0x157c - 0x158b: */
4845       /* EL_SAND */
4846
4847       /* 0x1590 - 0x159f: */
4848       /* EL_DC_LANDMINE */
4849
4850     case 0x15a0:
4851       element = EL_EM_DYNAMITE;
4852       break;
4853
4854     case 0x15a1:        /* key (red) */
4855       element = EL_EM_KEY_1;
4856       break;
4857
4858     case 0x15a2:        /* key (yellow) */
4859       element = EL_EM_KEY_2;
4860       break;
4861
4862     case 0x15a3:        /* key (blue) */
4863       element = EL_EM_KEY_4;
4864       break;
4865
4866     case 0x15a4:        /* key (green) */
4867       element = EL_EM_KEY_3;
4868       break;
4869
4870     case 0x15a5:        /* key (white) */
4871       element = EL_DC_KEY_WHITE;
4872       break;
4873
4874     case 0x15a6:
4875       element = EL_WALL_SLIPPERY;
4876       break;
4877
4878     case 0x15a7:
4879       element = EL_WALL;
4880       break;
4881
4882     case 0x15a8:        /* wall (not round) */
4883       element = EL_WALL;
4884       break;
4885
4886     case 0x15a9:        /* (blue) */
4887       element = EL_CHAR_A;
4888       break;
4889
4890     case 0x15aa:        /* (blue) */
4891       element = EL_CHAR_B;
4892       break;
4893
4894     case 0x15ab:        /* (blue) */
4895       element = EL_CHAR_C;
4896       break;
4897
4898     case 0x15ac:        /* (blue) */
4899       element = EL_CHAR_D;
4900       break;
4901
4902     case 0x15ad:        /* (blue) */
4903       element = EL_CHAR_E;
4904       break;
4905
4906     case 0x15ae:        /* (blue) */
4907       element = EL_CHAR_F;
4908       break;
4909
4910     case 0x15af:        /* (blue) */
4911       element = EL_CHAR_G;
4912       break;
4913
4914     case 0x15b0:        /* (blue) */
4915       element = EL_CHAR_H;
4916       break;
4917
4918     case 0x15b1:        /* (blue) */
4919       element = EL_CHAR_I;
4920       break;
4921
4922     case 0x15b2:        /* (blue) */
4923       element = EL_CHAR_J;
4924       break;
4925
4926     case 0x15b3:        /* (blue) */
4927       element = EL_CHAR_K;
4928       break;
4929
4930     case 0x15b4:        /* (blue) */
4931       element = EL_CHAR_L;
4932       break;
4933
4934     case 0x15b5:        /* (blue) */
4935       element = EL_CHAR_M;
4936       break;
4937
4938     case 0x15b6:        /* (blue) */
4939       element = EL_CHAR_N;
4940       break;
4941
4942     case 0x15b7:        /* (blue) */
4943       element = EL_CHAR_O;
4944       break;
4945
4946     case 0x15b8:        /* (blue) */
4947       element = EL_CHAR_P;
4948       break;
4949
4950     case 0x15b9:        /* (blue) */
4951       element = EL_CHAR_Q;
4952       break;
4953
4954     case 0x15ba:        /* (blue) */
4955       element = EL_CHAR_R;
4956       break;
4957
4958     case 0x15bb:        /* (blue) */
4959       element = EL_CHAR_S;
4960       break;
4961
4962     case 0x15bc:        /* (blue) */
4963       element = EL_CHAR_T;
4964       break;
4965
4966     case 0x15bd:        /* (blue) */
4967       element = EL_CHAR_U;
4968       break;
4969
4970     case 0x15be:        /* (blue) */
4971       element = EL_CHAR_V;
4972       break;
4973
4974     case 0x15bf:        /* (blue) */
4975       element = EL_CHAR_W;
4976       break;
4977
4978     case 0x15c0:        /* (blue) */
4979       element = EL_CHAR_X;
4980       break;
4981
4982     case 0x15c1:        /* (blue) */
4983       element = EL_CHAR_Y;
4984       break;
4985
4986     case 0x15c2:        /* (blue) */
4987       element = EL_CHAR_Z;
4988       break;
4989
4990     case 0x15c3:        /* (blue) */
4991       element = EL_CHAR_AUMLAUT;
4992       break;
4993
4994     case 0x15c4:        /* (blue) */
4995       element = EL_CHAR_OUMLAUT;
4996       break;
4997
4998     case 0x15c5:        /* (blue) */
4999       element = EL_CHAR_UUMLAUT;
5000       break;
5001
5002     case 0x15c6:        /* (blue) */
5003       element = EL_CHAR_0;
5004       break;
5005
5006     case 0x15c7:        /* (blue) */
5007       element = EL_CHAR_1;
5008       break;
5009
5010     case 0x15c8:        /* (blue) */
5011       element = EL_CHAR_2;
5012       break;
5013
5014     case 0x15c9:        /* (blue) */
5015       element = EL_CHAR_3;
5016       break;
5017
5018     case 0x15ca:        /* (blue) */
5019       element = EL_CHAR_4;
5020       break;
5021
5022     case 0x15cb:        /* (blue) */
5023       element = EL_CHAR_5;
5024       break;
5025
5026     case 0x15cc:        /* (blue) */
5027       element = EL_CHAR_6;
5028       break;
5029
5030     case 0x15cd:        /* (blue) */
5031       element = EL_CHAR_7;
5032       break;
5033
5034     case 0x15ce:        /* (blue) */
5035       element = EL_CHAR_8;
5036       break;
5037
5038     case 0x15cf:        /* (blue) */
5039       element = EL_CHAR_9;
5040       break;
5041
5042     case 0x15d0:        /* (blue) */
5043       element = EL_CHAR_PERIOD;
5044       break;
5045
5046     case 0x15d1:        /* (blue) */
5047       element = EL_CHAR_EXCLAM;
5048       break;
5049
5050     case 0x15d2:        /* (blue) */
5051       element = EL_CHAR_COLON;
5052       break;
5053
5054     case 0x15d3:        /* (blue) */
5055       element = EL_CHAR_LESS;
5056       break;
5057
5058     case 0x15d4:        /* (blue) */
5059       element = EL_CHAR_GREATER;
5060       break;
5061
5062     case 0x15d5:        /* (blue) */
5063       element = EL_CHAR_QUESTION;
5064       break;
5065
5066     case 0x15d6:        /* (blue) */
5067       element = EL_CHAR_COPYRIGHT;
5068       break;
5069
5070     case 0x15d7:        /* (blue) */
5071       element = EL_CHAR_UP;
5072       break;
5073
5074     case 0x15d8:        /* (blue) */
5075       element = EL_CHAR_DOWN;
5076       break;
5077
5078     case 0x15d9:        /* (blue) */
5079       element = EL_CHAR_BUTTON;
5080       break;
5081
5082     case 0x15da:        /* (blue) */
5083       element = EL_CHAR_PLUS;
5084       break;
5085
5086     case 0x15db:        /* (blue) */
5087       element = EL_CHAR_MINUS;
5088       break;
5089
5090     case 0x15dc:        /* (blue) */
5091       element = EL_CHAR_APOSTROPHE;
5092       break;
5093
5094     case 0x15dd:        /* (blue) */
5095       element = EL_CHAR_PARENLEFT;
5096       break;
5097
5098     case 0x15de:        /* (blue) */
5099       element = EL_CHAR_PARENRIGHT;
5100       break;
5101
5102     case 0x15df:        /* (green) */
5103       element = EL_CHAR_A;
5104       break;
5105
5106     case 0x15e0:        /* (green) */
5107       element = EL_CHAR_B;
5108       break;
5109
5110     case 0x15e1:        /* (green) */
5111       element = EL_CHAR_C;
5112       break;
5113
5114     case 0x15e2:        /* (green) */
5115       element = EL_CHAR_D;
5116       break;
5117
5118     case 0x15e3:        /* (green) */
5119       element = EL_CHAR_E;
5120       break;
5121
5122     case 0x15e4:        /* (green) */
5123       element = EL_CHAR_F;
5124       break;
5125
5126     case 0x15e5:        /* (green) */
5127       element = EL_CHAR_G;
5128       break;
5129
5130     case 0x15e6:        /* (green) */
5131       element = EL_CHAR_H;
5132       break;
5133
5134     case 0x15e7:        /* (green) */
5135       element = EL_CHAR_I;
5136       break;
5137
5138     case 0x15e8:        /* (green) */
5139       element = EL_CHAR_J;
5140       break;
5141
5142     case 0x15e9:        /* (green) */
5143       element = EL_CHAR_K;
5144       break;
5145
5146     case 0x15ea:        /* (green) */
5147       element = EL_CHAR_L;
5148       break;
5149
5150     case 0x15eb:        /* (green) */
5151       element = EL_CHAR_M;
5152       break;
5153
5154     case 0x15ec:        /* (green) */
5155       element = EL_CHAR_N;
5156       break;
5157
5158     case 0x15ed:        /* (green) */
5159       element = EL_CHAR_O;
5160       break;
5161
5162     case 0x15ee:        /* (green) */
5163       element = EL_CHAR_P;
5164       break;
5165
5166     case 0x15ef:        /* (green) */
5167       element = EL_CHAR_Q;
5168       break;
5169
5170     case 0x15f0:        /* (green) */
5171       element = EL_CHAR_R;
5172       break;
5173
5174     case 0x15f1:        /* (green) */
5175       element = EL_CHAR_S;
5176       break;
5177
5178     case 0x15f2:        /* (green) */
5179       element = EL_CHAR_T;
5180       break;
5181
5182     case 0x15f3:        /* (green) */
5183       element = EL_CHAR_U;
5184       break;
5185
5186     case 0x15f4:        /* (green) */
5187       element = EL_CHAR_V;
5188       break;
5189
5190     case 0x15f5:        /* (green) */
5191       element = EL_CHAR_W;
5192       break;
5193
5194     case 0x15f6:        /* (green) */
5195       element = EL_CHAR_X;
5196       break;
5197
5198     case 0x15f7:        /* (green) */
5199       element = EL_CHAR_Y;
5200       break;
5201
5202     case 0x15f8:        /* (green) */
5203       element = EL_CHAR_Z;
5204       break;
5205
5206     case 0x15f9:        /* (green) */
5207       element = EL_CHAR_AUMLAUT;
5208       break;
5209
5210     case 0x15fa:        /* (green) */
5211       element = EL_CHAR_OUMLAUT;
5212       break;
5213
5214     case 0x15fb:        /* (green) */
5215       element = EL_CHAR_UUMLAUT;
5216       break;
5217
5218     case 0x15fc:        /* (green) */
5219       element = EL_CHAR_0;
5220       break;
5221
5222     case 0x15fd:        /* (green) */
5223       element = EL_CHAR_1;
5224       break;
5225
5226     case 0x15fe:        /* (green) */
5227       element = EL_CHAR_2;
5228       break;
5229
5230     case 0x15ff:        /* (green) */
5231       element = EL_CHAR_3;
5232       break;
5233
5234     case 0x1600:        /* (green) */
5235       element = EL_CHAR_4;
5236       break;
5237
5238     case 0x1601:        /* (green) */
5239       element = EL_CHAR_5;
5240       break;
5241
5242     case 0x1602:        /* (green) */
5243       element = EL_CHAR_6;
5244       break;
5245
5246     case 0x1603:        /* (green) */
5247       element = EL_CHAR_7;
5248       break;
5249
5250     case 0x1604:        /* (green) */
5251       element = EL_CHAR_8;
5252       break;
5253
5254     case 0x1605:        /* (green) */
5255       element = EL_CHAR_9;
5256       break;
5257
5258     case 0x1606:        /* (green) */
5259       element = EL_CHAR_PERIOD;
5260       break;
5261
5262     case 0x1607:        /* (green) */
5263       element = EL_CHAR_EXCLAM;
5264       break;
5265
5266     case 0x1608:        /* (green) */
5267       element = EL_CHAR_COLON;
5268       break;
5269
5270     case 0x1609:        /* (green) */
5271       element = EL_CHAR_LESS;
5272       break;
5273
5274     case 0x160a:        /* (green) */
5275       element = EL_CHAR_GREATER;
5276       break;
5277
5278     case 0x160b:        /* (green) */
5279       element = EL_CHAR_QUESTION;
5280       break;
5281
5282     case 0x160c:        /* (green) */
5283       element = EL_CHAR_COPYRIGHT;
5284       break;
5285
5286     case 0x160d:        /* (green) */
5287       element = EL_CHAR_UP;
5288       break;
5289
5290     case 0x160e:        /* (green) */
5291       element = EL_CHAR_DOWN;
5292       break;
5293
5294     case 0x160f:        /* (green) */
5295       element = EL_CHAR_BUTTON;
5296       break;
5297
5298     case 0x1610:        /* (green) */
5299       element = EL_CHAR_PLUS;
5300       break;
5301
5302     case 0x1611:        /* (green) */
5303       element = EL_CHAR_MINUS;
5304       break;
5305
5306     case 0x1612:        /* (green) */
5307       element = EL_CHAR_APOSTROPHE;
5308       break;
5309
5310     case 0x1613:        /* (green) */
5311       element = EL_CHAR_PARENLEFT;
5312       break;
5313
5314     case 0x1614:        /* (green) */
5315       element = EL_CHAR_PARENRIGHT;
5316       break;
5317
5318     case 0x1615:        /* (blue steel) */
5319       element = EL_STEEL_CHAR_A;
5320       break;
5321
5322     case 0x1616:        /* (blue steel) */
5323       element = EL_STEEL_CHAR_B;
5324       break;
5325
5326     case 0x1617:        /* (blue steel) */
5327       element = EL_STEEL_CHAR_C;
5328       break;
5329
5330     case 0x1618:        /* (blue steel) */
5331       element = EL_STEEL_CHAR_D;
5332       break;
5333
5334     case 0x1619:        /* (blue steel) */
5335       element = EL_STEEL_CHAR_E;
5336       break;
5337
5338     case 0x161a:        /* (blue steel) */
5339       element = EL_STEEL_CHAR_F;
5340       break;
5341
5342     case 0x161b:        /* (blue steel) */
5343       element = EL_STEEL_CHAR_G;
5344       break;
5345
5346     case 0x161c:        /* (blue steel) */
5347       element = EL_STEEL_CHAR_H;
5348       break;
5349
5350     case 0x161d:        /* (blue steel) */
5351       element = EL_STEEL_CHAR_I;
5352       break;
5353
5354     case 0x161e:        /* (blue steel) */
5355       element = EL_STEEL_CHAR_J;
5356       break;
5357
5358     case 0x161f:        /* (blue steel) */
5359       element = EL_STEEL_CHAR_K;
5360       break;
5361
5362     case 0x1620:        /* (blue steel) */
5363       element = EL_STEEL_CHAR_L;
5364       break;
5365
5366     case 0x1621:        /* (blue steel) */
5367       element = EL_STEEL_CHAR_M;
5368       break;
5369
5370     case 0x1622:        /* (blue steel) */
5371       element = EL_STEEL_CHAR_N;
5372       break;
5373
5374     case 0x1623:        /* (blue steel) */
5375       element = EL_STEEL_CHAR_O;
5376       break;
5377
5378     case 0x1624:        /* (blue steel) */
5379       element = EL_STEEL_CHAR_P;
5380       break;
5381
5382     case 0x1625:        /* (blue steel) */
5383       element = EL_STEEL_CHAR_Q;
5384       break;
5385
5386     case 0x1626:        /* (blue steel) */
5387       element = EL_STEEL_CHAR_R;
5388       break;
5389
5390     case 0x1627:        /* (blue steel) */
5391       element = EL_STEEL_CHAR_S;
5392       break;
5393
5394     case 0x1628:        /* (blue steel) */
5395       element = EL_STEEL_CHAR_T;
5396       break;
5397
5398     case 0x1629:        /* (blue steel) */
5399       element = EL_STEEL_CHAR_U;
5400       break;
5401
5402     case 0x162a:        /* (blue steel) */
5403       element = EL_STEEL_CHAR_V;
5404       break;
5405
5406     case 0x162b:        /* (blue steel) */
5407       element = EL_STEEL_CHAR_W;
5408       break;
5409
5410     case 0x162c:        /* (blue steel) */
5411       element = EL_STEEL_CHAR_X;
5412       break;
5413
5414     case 0x162d:        /* (blue steel) */
5415       element = EL_STEEL_CHAR_Y;
5416       break;
5417
5418     case 0x162e:        /* (blue steel) */
5419       element = EL_STEEL_CHAR_Z;
5420       break;
5421
5422     case 0x162f:        /* (blue steel) */
5423       element = EL_STEEL_CHAR_AUMLAUT;
5424       break;
5425
5426     case 0x1630:        /* (blue steel) */
5427       element = EL_STEEL_CHAR_OUMLAUT;
5428       break;
5429
5430     case 0x1631:        /* (blue steel) */
5431       element = EL_STEEL_CHAR_UUMLAUT;
5432       break;
5433
5434     case 0x1632:        /* (blue steel) */
5435       element = EL_STEEL_CHAR_0;
5436       break;
5437
5438     case 0x1633:        /* (blue steel) */
5439       element = EL_STEEL_CHAR_1;
5440       break;
5441
5442     case 0x1634:        /* (blue steel) */
5443       element = EL_STEEL_CHAR_2;
5444       break;
5445
5446     case 0x1635:        /* (blue steel) */
5447       element = EL_STEEL_CHAR_3;
5448       break;
5449
5450     case 0x1636:        /* (blue steel) */
5451       element = EL_STEEL_CHAR_4;
5452       break;
5453
5454     case 0x1637:        /* (blue steel) */
5455       element = EL_STEEL_CHAR_5;
5456       break;
5457
5458     case 0x1638:        /* (blue steel) */
5459       element = EL_STEEL_CHAR_6;
5460       break;
5461
5462     case 0x1639:        /* (blue steel) */
5463       element = EL_STEEL_CHAR_7;
5464       break;
5465
5466     case 0x163a:        /* (blue steel) */
5467       element = EL_STEEL_CHAR_8;
5468       break;
5469
5470     case 0x163b:        /* (blue steel) */
5471       element = EL_STEEL_CHAR_9;
5472       break;
5473
5474     case 0x163c:        /* (blue steel) */
5475       element = EL_STEEL_CHAR_PERIOD;
5476       break;
5477
5478     case 0x163d:        /* (blue steel) */
5479       element = EL_STEEL_CHAR_EXCLAM;
5480       break;
5481
5482     case 0x163e:        /* (blue steel) */
5483       element = EL_STEEL_CHAR_COLON;
5484       break;
5485
5486     case 0x163f:        /* (blue steel) */
5487       element = EL_STEEL_CHAR_LESS;
5488       break;
5489
5490     case 0x1640:        /* (blue steel) */
5491       element = EL_STEEL_CHAR_GREATER;
5492       break;
5493
5494     case 0x1641:        /* (blue steel) */
5495       element = EL_STEEL_CHAR_QUESTION;
5496       break;
5497
5498     case 0x1642:        /* (blue steel) */
5499       element = EL_STEEL_CHAR_COPYRIGHT;
5500       break;
5501
5502     case 0x1643:        /* (blue steel) */
5503       element = EL_STEEL_CHAR_UP;
5504       break;
5505
5506     case 0x1644:        /* (blue steel) */
5507       element = EL_STEEL_CHAR_DOWN;
5508       break;
5509
5510     case 0x1645:        /* (blue steel) */
5511       element = EL_STEEL_CHAR_BUTTON;
5512       break;
5513
5514     case 0x1646:        /* (blue steel) */
5515       element = EL_STEEL_CHAR_PLUS;
5516       break;
5517
5518     case 0x1647:        /* (blue steel) */
5519       element = EL_STEEL_CHAR_MINUS;
5520       break;
5521
5522     case 0x1648:        /* (blue steel) */
5523       element = EL_STEEL_CHAR_APOSTROPHE;
5524       break;
5525
5526     case 0x1649:        /* (blue steel) */
5527       element = EL_STEEL_CHAR_PARENLEFT;
5528       break;
5529
5530     case 0x164a:        /* (blue steel) */
5531       element = EL_STEEL_CHAR_PARENRIGHT;
5532       break;
5533
5534     case 0x164b:        /* (green steel) */
5535       element = EL_STEEL_CHAR_A;
5536       break;
5537
5538     case 0x164c:        /* (green steel) */
5539       element = EL_STEEL_CHAR_B;
5540       break;
5541
5542     case 0x164d:        /* (green steel) */
5543       element = EL_STEEL_CHAR_C;
5544       break;
5545
5546     case 0x164e:        /* (green steel) */
5547       element = EL_STEEL_CHAR_D;
5548       break;
5549
5550     case 0x164f:        /* (green steel) */
5551       element = EL_STEEL_CHAR_E;
5552       break;
5553
5554     case 0x1650:        /* (green steel) */
5555       element = EL_STEEL_CHAR_F;
5556       break;
5557
5558     case 0x1651:        /* (green steel) */
5559       element = EL_STEEL_CHAR_G;
5560       break;
5561
5562     case 0x1652:        /* (green steel) */
5563       element = EL_STEEL_CHAR_H;
5564       break;
5565
5566     case 0x1653:        /* (green steel) */
5567       element = EL_STEEL_CHAR_I;
5568       break;
5569
5570     case 0x1654:        /* (green steel) */
5571       element = EL_STEEL_CHAR_J;
5572       break;
5573
5574     case 0x1655:        /* (green steel) */
5575       element = EL_STEEL_CHAR_K;
5576       break;
5577
5578     case 0x1656:        /* (green steel) */
5579       element = EL_STEEL_CHAR_L;
5580       break;
5581
5582     case 0x1657:        /* (green steel) */
5583       element = EL_STEEL_CHAR_M;
5584       break;
5585
5586     case 0x1658:        /* (green steel) */
5587       element = EL_STEEL_CHAR_N;
5588       break;
5589
5590     case 0x1659:        /* (green steel) */
5591       element = EL_STEEL_CHAR_O;
5592       break;
5593
5594     case 0x165a:        /* (green steel) */
5595       element = EL_STEEL_CHAR_P;
5596       break;
5597
5598     case 0x165b:        /* (green steel) */
5599       element = EL_STEEL_CHAR_Q;
5600       break;
5601
5602     case 0x165c:        /* (green steel) */
5603       element = EL_STEEL_CHAR_R;
5604       break;
5605
5606     case 0x165d:        /* (green steel) */
5607       element = EL_STEEL_CHAR_S;
5608       break;
5609
5610     case 0x165e:        /* (green steel) */
5611       element = EL_STEEL_CHAR_T;
5612       break;
5613
5614     case 0x165f:        /* (green steel) */
5615       element = EL_STEEL_CHAR_U;
5616       break;
5617
5618     case 0x1660:        /* (green steel) */
5619       element = EL_STEEL_CHAR_V;
5620       break;
5621
5622     case 0x1661:        /* (green steel) */
5623       element = EL_STEEL_CHAR_W;
5624       break;
5625
5626     case 0x1662:        /* (green steel) */
5627       element = EL_STEEL_CHAR_X;
5628       break;
5629
5630     case 0x1663:        /* (green steel) */
5631       element = EL_STEEL_CHAR_Y;
5632       break;
5633
5634     case 0x1664:        /* (green steel) */
5635       element = EL_STEEL_CHAR_Z;
5636       break;
5637
5638     case 0x1665:        /* (green steel) */
5639       element = EL_STEEL_CHAR_AUMLAUT;
5640       break;
5641
5642     case 0x1666:        /* (green steel) */
5643       element = EL_STEEL_CHAR_OUMLAUT;
5644       break;
5645
5646     case 0x1667:        /* (green steel) */
5647       element = EL_STEEL_CHAR_UUMLAUT;
5648       break;
5649
5650     case 0x1668:        /* (green steel) */
5651       element = EL_STEEL_CHAR_0;
5652       break;
5653
5654     case 0x1669:        /* (green steel) */
5655       element = EL_STEEL_CHAR_1;
5656       break;
5657
5658     case 0x166a:        /* (green steel) */
5659       element = EL_STEEL_CHAR_2;
5660       break;
5661
5662     case 0x166b:        /* (green steel) */
5663       element = EL_STEEL_CHAR_3;
5664       break;
5665
5666     case 0x166c:        /* (green steel) */
5667       element = EL_STEEL_CHAR_4;
5668       break;
5669
5670     case 0x166d:        /* (green steel) */
5671       element = EL_STEEL_CHAR_5;
5672       break;
5673
5674     case 0x166e:        /* (green steel) */
5675       element = EL_STEEL_CHAR_6;
5676       break;
5677
5678     case 0x166f:        /* (green steel) */
5679       element = EL_STEEL_CHAR_7;
5680       break;
5681
5682     case 0x1670:        /* (green steel) */
5683       element = EL_STEEL_CHAR_8;
5684       break;
5685
5686     case 0x1671:        /* (green steel) */
5687       element = EL_STEEL_CHAR_9;
5688       break;
5689
5690     case 0x1672:        /* (green steel) */
5691       element = EL_STEEL_CHAR_PERIOD;
5692       break;
5693
5694     case 0x1673:        /* (green steel) */
5695       element = EL_STEEL_CHAR_EXCLAM;
5696       break;
5697
5698     case 0x1674:        /* (green steel) */
5699       element = EL_STEEL_CHAR_COLON;
5700       break;
5701
5702     case 0x1675:        /* (green steel) */
5703       element = EL_STEEL_CHAR_LESS;
5704       break;
5705
5706     case 0x1676:        /* (green steel) */
5707       element = EL_STEEL_CHAR_GREATER;
5708       break;
5709
5710     case 0x1677:        /* (green steel) */
5711       element = EL_STEEL_CHAR_QUESTION;
5712       break;
5713
5714     case 0x1678:        /* (green steel) */
5715       element = EL_STEEL_CHAR_COPYRIGHT;
5716       break;
5717
5718     case 0x1679:        /* (green steel) */
5719       element = EL_STEEL_CHAR_UP;
5720       break;
5721
5722     case 0x167a:        /* (green steel) */
5723       element = EL_STEEL_CHAR_DOWN;
5724       break;
5725
5726     case 0x167b:        /* (green steel) */
5727       element = EL_STEEL_CHAR_BUTTON;
5728       break;
5729
5730     case 0x167c:        /* (green steel) */
5731       element = EL_STEEL_CHAR_PLUS;
5732       break;
5733
5734     case 0x167d:        /* (green steel) */
5735       element = EL_STEEL_CHAR_MINUS;
5736       break;
5737
5738     case 0x167e:        /* (green steel) */
5739       element = EL_STEEL_CHAR_APOSTROPHE;
5740       break;
5741
5742     case 0x167f:        /* (green steel) */
5743       element = EL_STEEL_CHAR_PARENLEFT;
5744       break;
5745
5746     case 0x1680:        /* (green steel) */
5747       element = EL_STEEL_CHAR_PARENRIGHT;
5748       break;
5749
5750     case 0x1681:        /* gate (red) */
5751       element = EL_EM_GATE_1;
5752       break;
5753
5754     case 0x1682:        /* secret gate (red) */
5755       element = EL_GATE_1_GRAY;
5756       break;
5757
5758     case 0x1683:        /* gate (yellow) */
5759       element = EL_EM_GATE_2;
5760       break;
5761
5762     case 0x1684:        /* secret gate (yellow) */
5763       element = EL_GATE_2_GRAY;
5764       break;
5765
5766     case 0x1685:        /* gate (blue) */
5767       element = EL_EM_GATE_4;
5768       break;
5769
5770     case 0x1686:        /* secret gate (blue) */
5771       element = EL_GATE_4_GRAY;
5772       break;
5773
5774     case 0x1687:        /* gate (green) */
5775       element = EL_EM_GATE_3;
5776       break;
5777
5778     case 0x1688:        /* secret gate (green) */
5779       element = EL_GATE_3_GRAY;
5780       break;
5781
5782     case 0x1689:        /* gate (white) */
5783       element = EL_DC_GATE_WHITE;
5784       break;
5785
5786     case 0x168a:        /* secret gate (white) */
5787       element = EL_DC_GATE_WHITE_GRAY;
5788       break;
5789
5790     case 0x168b:        /* secret gate (no key) */
5791       element = EL_DC_GATE_FAKE_GRAY;
5792       break;
5793
5794     case 0x168c:
5795       element = EL_ROBOT_WHEEL;
5796       break;
5797
5798     case 0x168d:
5799       element = EL_DC_TIMEGATE_SWITCH;
5800       break;
5801
5802     case 0x168e:
5803       element = EL_ACID_POOL_BOTTOM;
5804       break;
5805
5806     case 0x168f:
5807       element = EL_ACID_POOL_TOPLEFT;
5808       break;
5809
5810     case 0x1690:
5811       element = EL_ACID_POOL_TOPRIGHT;
5812       break;
5813
5814     case 0x1691:
5815       element = EL_ACID_POOL_BOTTOMLEFT;
5816       break;
5817
5818     case 0x1692:
5819       element = EL_ACID_POOL_BOTTOMRIGHT;
5820       break;
5821
5822     case 0x1693:
5823       element = EL_STEELWALL;
5824       break;
5825
5826     case 0x1694:
5827       element = EL_STEELWALL_SLIPPERY;
5828       break;
5829
5830     case 0x1695:        /* steel wall (not round) */
5831       element = EL_STEELWALL;
5832       break;
5833
5834     case 0x1696:        /* steel wall (left) */
5835       element = EL_DC_STEELWALL_1_LEFT;
5836       break;
5837
5838     case 0x1697:        /* steel wall (bottom) */
5839       element = EL_DC_STEELWALL_1_BOTTOM;
5840       break;
5841
5842     case 0x1698:        /* steel wall (right) */
5843       element = EL_DC_STEELWALL_1_RIGHT;
5844       break;
5845
5846     case 0x1699:        /* steel wall (top) */
5847       element = EL_DC_STEELWALL_1_TOP;
5848       break;
5849
5850     case 0x169a:        /* steel wall (left/bottom) */
5851       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5852       break;
5853
5854     case 0x169b:        /* steel wall (right/bottom) */
5855       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5856       break;
5857
5858     case 0x169c:        /* steel wall (right/top) */
5859       element = EL_DC_STEELWALL_1_TOPRIGHT;
5860       break;
5861
5862     case 0x169d:        /* steel wall (left/top) */
5863       element = EL_DC_STEELWALL_1_TOPLEFT;
5864       break;
5865
5866     case 0x169e:        /* steel wall (right/bottom small) */
5867       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5868       break;
5869
5870     case 0x169f:        /* steel wall (left/bottom small) */
5871       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5872       break;
5873
5874     case 0x16a0:        /* steel wall (right/top small) */
5875       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5876       break;
5877
5878     case 0x16a1:        /* steel wall (left/top small) */
5879       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5880       break;
5881
5882     case 0x16a2:        /* steel wall (left/right) */
5883       element = EL_DC_STEELWALL_1_VERTICAL;
5884       break;
5885
5886     case 0x16a3:        /* steel wall (top/bottom) */
5887       element = EL_DC_STEELWALL_1_HORIZONTAL;
5888       break;
5889
5890     case 0x16a4:        /* steel wall 2 (left end) */
5891       element = EL_DC_STEELWALL_2_LEFT;
5892       break;
5893
5894     case 0x16a5:        /* steel wall 2 (right end) */
5895       element = EL_DC_STEELWALL_2_RIGHT;
5896       break;
5897
5898     case 0x16a6:        /* steel wall 2 (top end) */
5899       element = EL_DC_STEELWALL_2_TOP;
5900       break;
5901
5902     case 0x16a7:        /* steel wall 2 (bottom end) */
5903       element = EL_DC_STEELWALL_2_BOTTOM;
5904       break;
5905
5906     case 0x16a8:        /* steel wall 2 (left/right) */
5907       element = EL_DC_STEELWALL_2_HORIZONTAL;
5908       break;
5909
5910     case 0x16a9:        /* steel wall 2 (up/down) */
5911       element = EL_DC_STEELWALL_2_VERTICAL;
5912       break;
5913
5914     case 0x16aa:        /* steel wall 2 (mid) */
5915       element = EL_DC_STEELWALL_2_MIDDLE;
5916       break;
5917
5918     case 0x16ab:
5919       element = EL_SIGN_EXCLAMATION;
5920       break;
5921
5922     case 0x16ac:
5923       element = EL_SIGN_RADIOACTIVITY;
5924       break;
5925
5926     case 0x16ad:
5927       element = EL_SIGN_STOP;
5928       break;
5929
5930     case 0x16ae:
5931       element = EL_SIGN_WHEELCHAIR;
5932       break;
5933
5934     case 0x16af:
5935       element = EL_SIGN_PARKING;
5936       break;
5937
5938     case 0x16b0:
5939       element = EL_SIGN_NO_ENTRY;
5940       break;
5941
5942     case 0x16b1:
5943       element = EL_SIGN_HEART;
5944       break;
5945
5946     case 0x16b2:
5947       element = EL_SIGN_GIVE_WAY;
5948       break;
5949
5950     case 0x16b3:
5951       element = EL_SIGN_ENTRY_FORBIDDEN;
5952       break;
5953
5954     case 0x16b4:
5955       element = EL_SIGN_EMERGENCY_EXIT;
5956       break;
5957
5958     case 0x16b5:
5959       element = EL_SIGN_YIN_YANG;
5960       break;
5961
5962     case 0x16b6:
5963       element = EL_WALL_EMERALD;
5964       break;
5965
5966     case 0x16b7:
5967       element = EL_WALL_DIAMOND;
5968       break;
5969
5970     case 0x16b8:
5971       element = EL_WALL_PEARL;
5972       break;
5973
5974     case 0x16b9:
5975       element = EL_WALL_CRYSTAL;
5976       break;
5977
5978     case 0x16ba:
5979       element = EL_INVISIBLE_WALL;
5980       break;
5981
5982     case 0x16bb:
5983       element = EL_INVISIBLE_STEELWALL;
5984       break;
5985
5986       /* 0x16bc - 0x16cb: */
5987       /* EL_INVISIBLE_SAND */
5988
5989     case 0x16cc:
5990       element = EL_LIGHT_SWITCH;
5991       break;
5992
5993     case 0x16cd:
5994       element = EL_ENVELOPE_1;
5995       break;
5996
5997     default:
5998       if (element >= 0x0117 && element <= 0x036e)       /* (?) */
5999         element = EL_DIAMOND;
6000       else if (element >= 0x042d && element <= 0x0684)  /* (?) */
6001         element = EL_EMERALD;
6002       else if (element >= 0x157c && element <= 0x158b)
6003         element = EL_SAND;
6004       else if (element >= 0x1590 && element <= 0x159f)
6005         element = EL_DC_LANDMINE;
6006       else if (element >= 0x16bc && element <= 0x16cb)
6007         element = EL_INVISIBLE_SAND;
6008       else
6009       {
6010         Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
6011         element = EL_UNKNOWN;
6012       }
6013       break;
6014   }
6015
6016   return getMappedElement(element);
6017 }
6018
6019 #if 1
6020
6021 static void LoadLevelFromFileStream_DC(FILE *file, struct LevelInfo *level,
6022                                        int nr)
6023 {
6024   byte header[DC_LEVEL_HEADER_SIZE];
6025   int envelope_size;
6026   int envelope_header_pos = 62;
6027   int envelope_content_pos = 94;
6028   int level_name_pos = 251;
6029   int level_author_pos = 292;
6030   int envelope_header_len;
6031   int envelope_content_len;
6032   int level_name_len;
6033   int level_author_len;
6034   int fieldx, fieldy;
6035   int num_yamyam_contents;
6036   int i, x, y;
6037
6038   getDecodedWord_DC(0, TRUE);           /* initialize DC2 decoding engine */
6039
6040   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6041   {
6042     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6043
6044     header[i * 2 + 0] = header_word >> 8;
6045     header[i * 2 + 1] = header_word & 0xff;
6046   }
6047
6048   /* read some values from level header to check level decoding integrity */
6049   fieldx = header[6] | (header[7] << 8);
6050   fieldy = header[8] | (header[9] << 8);
6051   num_yamyam_contents = header[60] | (header[61] << 8);
6052
6053   /* do some simple sanity checks to ensure that level was correctly decoded */
6054   if (fieldx < 1 || fieldx > 256 ||
6055       fieldy < 1 || fieldy > 256 ||
6056       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6057   {
6058     level->no_valid_file = TRUE;
6059
6060     Error(ERR_WARN, "cannot decode level from stream -- using empty level");
6061
6062     return;
6063   }
6064
6065   /* maximum envelope header size is 31 bytes */
6066   envelope_header_len   = header[envelope_header_pos];
6067   /* maximum envelope content size is 110 (156?) bytes */
6068   envelope_content_len  = header[envelope_content_pos];
6069
6070   /* maximum level title size is 40 bytes */
6071   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6072   /* maximum level author size is 30 (51?) bytes */
6073   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6074
6075   envelope_size = 0;
6076
6077   for (i = 0; i < envelope_header_len; i++)
6078     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6079       level->envelope[0].text[envelope_size++] =
6080         header[envelope_header_pos + 1 + i];
6081
6082   if (envelope_header_len > 0 && envelope_content_len > 0)
6083   {
6084     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6085       level->envelope[0].text[envelope_size++] = '\n';
6086     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6087       level->envelope[0].text[envelope_size++] = '\n';
6088   }
6089
6090   for (i = 0; i < envelope_content_len; i++)
6091     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6092       level->envelope[0].text[envelope_size++] =
6093         header[envelope_content_pos + 1 + i];
6094
6095   level->envelope[0].text[envelope_size] = '\0';
6096
6097   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6098   level->envelope[0].ysize = 10;
6099   level->envelope[0].autowrap = TRUE;
6100   level->envelope[0].centered = TRUE;
6101
6102   for (i = 0; i < level_name_len; i++)
6103     level->name[i] = header[level_name_pos + 1 + i];
6104   level->name[level_name_len] = '\0';
6105
6106   for (i = 0; i < level_author_len; i++)
6107     level->author[i] = header[level_author_pos + 1 + i];
6108   level->author[level_author_len] = '\0';
6109
6110   num_yamyam_contents = header[60] | (header[61] << 8);
6111   level->num_yamyam_contents =
6112     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6113
6114   for (i = 0; i < num_yamyam_contents; i++)
6115   {
6116     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6117     {
6118       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6119 #if 1
6120       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6121 #else
6122       int element_dc = word;
6123 #endif
6124
6125       if (i < MAX_ELEMENT_CONTENTS)
6126         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6127     }
6128   }
6129
6130   fieldx = header[6] | (header[7] << 8);
6131   fieldy = header[8] | (header[9] << 8);
6132   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6133   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6134
6135   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6136   {
6137     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6138 #if 1
6139     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6140 #else
6141     int element_dc = word;
6142 #endif
6143
6144     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6145       level->field[x][y] = getMappedElement_DC(element_dc);
6146   }
6147
6148   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6149   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6150   level->field[x][y] = EL_PLAYER_1;
6151
6152   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6153   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6154   level->field[x][y] = EL_PLAYER_2;
6155
6156   level->gems_needed            = header[18] | (header[19] << 8);
6157
6158   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6159   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6160   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6161   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6162   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6163   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6164   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6165   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6166   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6167   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6168   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6169   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6170
6171   level->time                   = header[44] | (header[45] << 8);
6172
6173   level->amoeba_speed           = header[46] | (header[47] << 8);
6174   level->time_light             = header[48] | (header[49] << 8);
6175   level->time_timegate          = header[50] | (header[51] << 8);
6176   level->time_wheel             = header[52] | (header[53] << 8);
6177   level->time_magic_wall        = header[54] | (header[55] << 8);
6178   level->extra_time             = header[56] | (header[57] << 8);
6179   level->shield_normal_time     = header[58] | (header[59] << 8);
6180
6181   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6182      can slip down from flat walls, like normal walls and steel walls */
6183   level->em_slippery_gems = TRUE;
6184
6185 #if 0
6186   /* Diamond Caves II levels are always surrounded by indestructible wall, but
6187      not necessarily in a rectangular way -- fill with invisible steel wall */
6188
6189   /* !!! not always true !!! keep level and set BorderElement instead !!! */
6190
6191   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6192   {
6193 #if 1
6194     if ((x == 0 || x == level->fieldx - 1 ||
6195          y == 0 || y == level->fieldy - 1) &&
6196         level->field[x][y] == EL_EMPTY)
6197       level->field[x][y] = EL_INVISIBLE_STEELWALL;
6198 #else
6199     if ((x == 0 || x == level->fieldx - 1 ||
6200          y == 0 || y == level->fieldy - 1) &&
6201         level->field[x][y] == EL_EMPTY)
6202       FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
6203                      level->field, level->fieldx, level->fieldy);
6204 #endif
6205   }
6206 #endif
6207 }
6208
6209 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6210                                      struct LevelFileInfo *level_file_info)
6211 {
6212   char *filename = level_file_info->filename;
6213   FILE *file;
6214   int num_magic_bytes = 8;
6215   char magic_bytes[num_magic_bytes + 1];
6216   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6217
6218   if (!(file = fopen(filename, MODE_READ)))
6219   {
6220     level->no_valid_file = TRUE;
6221
6222     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6223
6224     return;
6225   }
6226
6227   // fseek(file, 0x0000, SEEK_SET);
6228
6229   if (level_file_info->packed)
6230   {
6231     /* read "magic bytes" from start of file */
6232     fgets(magic_bytes, num_magic_bytes + 1, file);
6233
6234     /* check "magic bytes" for correct file format */
6235     if (!strPrefix(magic_bytes, "DC2"))
6236     {
6237       level->no_valid_file = TRUE;
6238
6239       Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
6240             filename);
6241
6242       return;
6243     }
6244
6245     if (strPrefix(magic_bytes, "DC2Win95") ||
6246         strPrefix(magic_bytes, "DC2Win98"))
6247     {
6248       int position_first_level = 0x00fa;
6249       int extra_bytes = 4;
6250       int skip_bytes;
6251
6252       /* advance file stream to first level inside the level package */
6253       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6254
6255       /* each block of level data is followed by block of non-level data */
6256       num_levels_to_skip *= 2;
6257
6258       /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
6259       while (num_levels_to_skip >= 0)
6260       {
6261         /* advance file stream to next level inside the level package */
6262         if (fseek(file, skip_bytes, SEEK_CUR) != 0)
6263         {
6264           level->no_valid_file = TRUE;
6265
6266           Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
6267                 filename);
6268
6269           return;
6270         }
6271
6272         /* skip apparently unused extra bytes following each level */
6273         ReadUnusedBytesFromFile(file, extra_bytes);
6274
6275         /* read size of next level in level package */
6276         skip_bytes = getFile32BitLE(file);
6277
6278         num_levels_to_skip--;
6279       }
6280     }
6281     else
6282     {
6283       level->no_valid_file = TRUE;
6284
6285       Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
6286             filename);
6287
6288       return;
6289     }
6290   }
6291
6292   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
6293
6294   fclose(file);
6295 }
6296
6297 #else
6298
6299 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6300                                      struct LevelFileInfo *level_file_info)
6301 {
6302   char *filename = level_file_info->filename;
6303   FILE *file;
6304 #if 0
6305   int nr = level_file_info->nr - leveldir_current->first_level;
6306 #endif
6307   byte header[DC_LEVEL_HEADER_SIZE];
6308   int envelope_size;
6309   int envelope_header_pos = 62;
6310   int envelope_content_pos = 94;
6311   int level_name_pos = 251;
6312   int level_author_pos = 292;
6313   int envelope_header_len;
6314   int envelope_content_len;
6315   int level_name_len;
6316   int level_author_len;
6317   int fieldx, fieldy;
6318   int num_yamyam_contents;
6319   int i, x, y;
6320
6321   if (!(file = fopen(filename, MODE_READ)))
6322   {
6323     level->no_valid_file = TRUE;
6324
6325     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6326
6327     return;
6328   }
6329
6330 #if 0
6331   /* position file stream to the requested level inside the level package */
6332   if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
6333   {
6334     level->no_valid_file = TRUE;
6335
6336     Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
6337
6338     return;
6339   }
6340 #endif
6341
6342   getDecodedWord_DC(0, TRUE);           /* initialize DC2 decoding engine */
6343
6344   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6345   {
6346     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6347
6348     header[i * 2 + 0] = header_word >> 8;
6349     header[i * 2 + 1] = header_word & 0xff;
6350   }
6351
6352   /* read some values from level header to check level decoding integrity */
6353   fieldx = header[6] | (header[7] << 8);
6354   fieldy = header[8] | (header[9] << 8);
6355   num_yamyam_contents = header[60] | (header[61] << 8);
6356
6357   /* do some simple sanity checks to ensure that level was correctly decoded */
6358   if (fieldx < 1 || fieldx > 256 ||
6359       fieldy < 1 || fieldy > 256 ||
6360       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6361   {
6362     level->no_valid_file = TRUE;
6363
6364     Error(ERR_WARN, "cannot read level from file '%s' -- using empty level",
6365           filename);
6366
6367     return;
6368   }
6369
6370   /* maximum envelope header size is 31 bytes */
6371   envelope_header_len   = header[envelope_header_pos];
6372   /* maximum envelope content size is 110 (156?) bytes */
6373   envelope_content_len  = header[envelope_content_pos];
6374
6375   /* maximum level title size is 40 bytes */
6376   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6377   /* maximum level author size is 30 (51?) bytes */
6378   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6379
6380   envelope_size = 0;
6381
6382   for (i = 0; i < envelope_header_len; i++)
6383     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6384       level->envelope[0].text[envelope_size++] =
6385         header[envelope_header_pos + 1 + i];
6386
6387   if (envelope_header_len > 0 && envelope_content_len > 0)
6388   {
6389     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6390       level->envelope[0].text[envelope_size++] = '\n';
6391     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6392       level->envelope[0].text[envelope_size++] = '\n';
6393   }
6394
6395   for (i = 0; i < envelope_content_len; i++)
6396     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6397       level->envelope[0].text[envelope_size++] =
6398         header[envelope_content_pos + 1 + i];
6399
6400   level->envelope[0].text[envelope_size] = '\0';
6401
6402   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6403   level->envelope[0].ysize = 10;
6404   level->envelope[0].autowrap = TRUE;
6405   level->envelope[0].centered = TRUE;
6406
6407   for (i = 0; i < level_name_len; i++)
6408     level->name[i] = header[level_name_pos + 1 + i];
6409   level->name[level_name_len] = '\0';
6410
6411   for (i = 0; i < level_author_len; i++)
6412     level->author[i] = header[level_author_pos + 1 + i];
6413   level->author[level_author_len] = '\0';
6414
6415   num_yamyam_contents = header[60] | (header[61] << 8);
6416   level->num_yamyam_contents =
6417     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6418
6419   for (i = 0; i < num_yamyam_contents; i++)
6420   {
6421     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6422     {
6423       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6424 #if 1
6425       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6426 #else
6427       int element_dc = word;
6428 #endif
6429
6430       if (i < MAX_ELEMENT_CONTENTS)
6431         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6432     }
6433   }
6434
6435   fieldx = header[6] | (header[7] << 8);
6436   fieldy = header[8] | (header[9] << 8);
6437   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6438   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6439
6440   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6441   {
6442     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6443 #if 1
6444     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6445 #else
6446     int element_dc = word;
6447 #endif
6448
6449     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6450       level->field[x][y] = getMappedElement_DC(element_dc);
6451   }
6452
6453   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6454   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6455   level->field[x][y] = EL_PLAYER_1;
6456
6457   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6458   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6459   level->field[x][y] = EL_PLAYER_2;
6460
6461   level->gems_needed            = header[18] | (header[19] << 8);
6462
6463   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6464   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6465   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6466   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6467   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6468   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6469   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6470   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6471   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6472   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6473   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6474   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6475
6476   level->time                   = header[44] | (header[45] << 8);
6477
6478   level->amoeba_speed           = header[46] | (header[47] << 8);
6479   level->time_light             = header[48] | (header[49] << 8);
6480   level->time_timegate          = header[50] | (header[51] << 8);
6481   level->time_wheel             = header[52] | (header[53] << 8);
6482   level->time_magic_wall        = header[54] | (header[55] << 8);
6483   level->extra_time             = header[56] | (header[57] << 8);
6484   level->shield_normal_time     = header[58] | (header[59] << 8);
6485
6486   fclose(file);
6487
6488   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6489      can slip down from flat walls, like normal walls and steel walls */
6490   level->em_slippery_gems = TRUE;
6491
6492 #if 0
6493   /* Diamond Caves II levels are always surrounded by indestructible wall, but
6494      not necessarily in a rectangular way -- fill with invisible steel wall */
6495
6496   /* !!! not always true !!! keep level and set BorderElement instead !!! */
6497
6498   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6499   {
6500 #if 1
6501     if ((x == 0 || x == level->fieldx - 1 ||
6502          y == 0 || y == level->fieldy - 1) &&
6503         level->field[x][y] == EL_EMPTY)
6504       level->field[x][y] = EL_INVISIBLE_STEELWALL;
6505 #else
6506     if ((x == 0 || x == level->fieldx - 1 ||
6507          y == 0 || y == level->fieldy - 1) &&
6508         level->field[x][y] == EL_EMPTY)
6509       FloodFillLevel(x, y, EL_INVISIBLE_STEELWALL,
6510                      level->field, level->fieldx, level->fieldy);
6511 #endif
6512   }
6513 #endif
6514 }
6515
6516 #endif
6517
6518
6519 /* ------------------------------------------------------------------------- */
6520 /* functions for loading SB level                                            */
6521 /* ------------------------------------------------------------------------- */
6522
6523 int getMappedElement_SB(int element_ascii, boolean use_ces)
6524 {
6525   static struct
6526   {
6527     int ascii;
6528     int sb;
6529     int ce;
6530   }
6531   sb_element_mapping[] =
6532   {
6533     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  /* floor (space) */
6534     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  /* wall */
6535     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  /* player */
6536     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  /* box */
6537     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  /* goal square */
6538     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  /* box on goal square */
6539     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  /* player on goal square */
6540 #if 0
6541     { '_', EL_INVISIBLE_STEELWALL,  EL_CUSTOM_8 },  /* floor beyond border */
6542 #else
6543     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  /* floor beyond border */
6544 #endif
6545
6546     { 0,   -1,                      -1          },
6547   };
6548
6549   int i;
6550
6551   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6552     if (element_ascii == sb_element_mapping[i].ascii)
6553       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6554
6555   return EL_UNDEFINED;
6556 }
6557
6558 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6559                                      struct LevelFileInfo *level_file_info)
6560 {
6561   char *filename = level_file_info->filename;
6562   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6563   char last_comment[MAX_LINE_LEN];
6564   char level_name[MAX_LINE_LEN];
6565   char *line_ptr;
6566   FILE *file;
6567   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6568   boolean read_continued_line = FALSE;
6569   boolean reading_playfield = FALSE;
6570   boolean got_valid_playfield_line = FALSE;
6571   boolean invalid_playfield_char = FALSE;
6572   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6573   int file_level_nr = 0;
6574   int line_nr = 0;
6575   int x, y;
6576
6577 #if 0
6578   printf("::: looking for level number %d [%d]\n",
6579          level_file_info->nr, num_levels_to_skip);
6580 #endif
6581
6582   last_comment[0] = '\0';
6583   level_name[0] = '\0';
6584
6585   if (!(file = fopen(filename, MODE_READ)))
6586   {
6587     level->no_valid_file = TRUE;
6588
6589     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6590
6591     return;
6592   }
6593
6594   while (!feof(file))
6595   {
6596     /* level successfully read, but next level may follow here */
6597     if (!got_valid_playfield_line && reading_playfield)
6598     {
6599 #if 0
6600       printf("::: read complete playfield\n");
6601 #endif
6602
6603       /* read playfield from single level file -- skip remaining file */
6604       if (!level_file_info->packed)
6605         break;
6606
6607       if (file_level_nr >= num_levels_to_skip)
6608         break;
6609
6610       file_level_nr++;
6611
6612       last_comment[0] = '\0';
6613       level_name[0] = '\0';
6614
6615       reading_playfield = FALSE;
6616     }
6617
6618     got_valid_playfield_line = FALSE;
6619
6620     /* read next line of input file */
6621     if (!fgets(line, MAX_LINE_LEN, file))
6622       break;
6623
6624     /* check if line was completely read and is terminated by line break */
6625     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6626       line_nr++;
6627
6628     /* cut trailing line break (this can be newline and/or carriage return) */
6629     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6630       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6631         *line_ptr = '\0';
6632
6633     /* copy raw input line for later use (mainly debugging output) */
6634     strcpy(line_raw, line);
6635
6636     if (read_continued_line)
6637     {
6638       /* append new line to existing line, if there is enough space */
6639       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6640         strcat(previous_line, line_ptr);
6641
6642       strcpy(line, previous_line);      /* copy storage buffer to line */
6643
6644       read_continued_line = FALSE;
6645     }
6646
6647     /* if the last character is '\', continue at next line */
6648     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6649     {
6650       line[strlen(line) - 1] = '\0';    /* cut off trailing backslash */
6651       strcpy(previous_line, line);      /* copy line to storage buffer */
6652
6653       read_continued_line = TRUE;
6654
6655       continue;
6656     }
6657
6658     /* skip empty lines */
6659     if (line[0] == '\0')
6660       continue;
6661
6662     /* extract comment text from comment line */
6663     if (line[0] == ';')
6664     {
6665       for (line_ptr = line; *line_ptr; line_ptr++)
6666         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6667           break;
6668
6669       strcpy(last_comment, line_ptr);
6670
6671 #if 0
6672       printf("::: found comment '%s' in line %d\n", last_comment, line_nr);
6673 #endif
6674
6675       continue;
6676     }
6677
6678     /* extract level title text from line containing level title */
6679     if (line[0] == '\'')
6680     {
6681       strcpy(level_name, &line[1]);
6682
6683       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6684         level_name[strlen(level_name) - 1] = '\0';
6685
6686 #if 0
6687       printf("::: found level name '%s' in line %d\n", level_name, line_nr);
6688 #endif
6689
6690       continue;
6691     }
6692
6693     /* skip lines containing only spaces (or empty lines) */
6694     for (line_ptr = line; *line_ptr; line_ptr++)
6695       if (*line_ptr != ' ')
6696         break;
6697     if (*line_ptr == '\0')
6698       continue;
6699
6700     /* at this point, we have found a line containing part of a playfield */
6701
6702 #if 0
6703     printf("::: found playfield row in line %d\n", line_nr);
6704 #endif
6705
6706     got_valid_playfield_line = TRUE;
6707
6708     if (!reading_playfield)
6709     {
6710       reading_playfield = TRUE;
6711       invalid_playfield_char = FALSE;
6712
6713       for (x = 0; x < MAX_LEV_FIELDX; x++)
6714         for (y = 0; y < MAX_LEV_FIELDY; y++)
6715           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6716
6717       level->fieldx = 0;
6718       level->fieldy = 0;
6719
6720       /* start with topmost tile row */
6721       y = 0;
6722     }
6723
6724     /* skip playfield line if larger row than allowed */
6725     if (y >= MAX_LEV_FIELDY)
6726       continue;
6727
6728     /* start with leftmost tile column */
6729     x = 0;
6730
6731     /* read playfield elements from line */
6732     for (line_ptr = line; *line_ptr; line_ptr++)
6733     {
6734       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6735
6736       /* stop parsing playfield line if larger column than allowed */
6737       if (x >= MAX_LEV_FIELDX)
6738         break;
6739
6740       if (mapped_sb_element == EL_UNDEFINED)
6741       {
6742         invalid_playfield_char = TRUE;
6743
6744         break;
6745       }
6746
6747       level->field[x][y] = mapped_sb_element;
6748
6749       /* continue with next tile column */
6750       x++;
6751
6752       level->fieldx = MAX(x, level->fieldx);
6753     }
6754
6755     if (invalid_playfield_char)
6756     {
6757       /* if first playfield line, treat invalid lines as comment lines */
6758       if (y == 0)
6759         reading_playfield = FALSE;
6760
6761       continue;
6762     }
6763
6764     /* continue with next tile row */
6765     y++;
6766   }
6767
6768   fclose(file);
6769
6770   level->fieldy = y;
6771
6772   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6773   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6774
6775   if (!reading_playfield)
6776   {
6777     level->no_valid_file = TRUE;
6778
6779     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6780
6781     return;
6782   }
6783
6784   if (*level_name != '\0')
6785   {
6786     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6787     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6788
6789 #if 0
6790     printf(":1: level name: '%s'\n", level->name);
6791 #endif
6792   }
6793   else if (*last_comment != '\0')
6794   {
6795     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6796     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6797
6798 #if 0
6799     printf(":2: level name: '%s'\n", level->name);
6800 #endif
6801   }
6802   else
6803   {
6804     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6805   }
6806
6807   /* set all empty fields beyond the border walls to invisible steel wall */
6808   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6809   {
6810     if ((x == 0 || x == level->fieldx - 1 ||
6811          y == 0 || y == level->fieldy - 1) &&
6812         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6813       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6814                      level->field, level->fieldx, level->fieldy);
6815   }
6816
6817   /* set special level settings for Sokoban levels */
6818
6819   level->time = 0;
6820   level->use_step_counter = TRUE;
6821
6822   if (load_xsb_to_ces)
6823   {
6824 #if 1
6825     /* !!! special global settings can now be set in level template !!! */
6826 #else
6827     level->initial_player_stepsize[0] = STEPSIZE_SLOW;
6828 #endif
6829
6830     /* fill smaller playfields with padding "beyond border wall" elements */
6831     if (level->fieldx < SCR_FIELDX ||
6832         level->fieldy < SCR_FIELDY)
6833     {
6834       short field[level->fieldx][level->fieldy];
6835       int new_fieldx = MAX(level->fieldx, SCR_FIELDX);
6836       int new_fieldy = MAX(level->fieldy, SCR_FIELDY);
6837       int pos_fieldx = (new_fieldx - level->fieldx) / 2;
6838       int pos_fieldy = (new_fieldy - level->fieldy) / 2;
6839
6840       /* copy old playfield (which is smaller than the visible area) */
6841       for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6842         field[x][y] = level->field[x][y];
6843
6844       /* fill new, larger playfield with "beyond border wall" elements */
6845       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
6846         level->field[x][y] = getMappedElement_SB('_', load_xsb_to_ces);
6847
6848       /* copy the old playfield to the middle of the new playfield */
6849       for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6850         level->field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
6851
6852       level->fieldx = new_fieldx;
6853       level->fieldy = new_fieldy;
6854     }
6855
6856     level->use_custom_template = TRUE;
6857   }
6858 }
6859
6860
6861 /* ------------------------------------------------------------------------- */
6862 /* functions for handling native levels                                      */
6863 /* ------------------------------------------------------------------------- */
6864
6865 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6866                                      struct LevelFileInfo *level_file_info)
6867 {
6868   if (!LoadNativeLevel_EM(level_file_info->filename))
6869     level->no_valid_file = TRUE;
6870 }
6871
6872 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6873                                      struct LevelFileInfo *level_file_info)
6874 {
6875   int pos = 0;
6876
6877   /* determine position of requested level inside level package */
6878   if (level_file_info->packed)
6879     pos = level_file_info->nr - leveldir_current->first_level;
6880
6881   if (!LoadNativeLevel_SP(level_file_info->filename, pos))
6882     level->no_valid_file = TRUE;
6883 }
6884
6885 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6886 {
6887   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6888     CopyNativeLevel_RND_to_EM(level);
6889   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6890     CopyNativeLevel_RND_to_SP(level);
6891 }
6892
6893 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6894 {
6895   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6896     CopyNativeLevel_EM_to_RND(level);
6897   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6898     CopyNativeLevel_SP_to_RND(level);
6899 }
6900
6901 void SaveNativeLevel(struct LevelInfo *level)
6902 {
6903   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6904   {
6905     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6906     char *filename = getLevelFilenameFromBasename(basename);
6907
6908     CopyNativeLevel_RND_to_SP(level);
6909     CopyNativeTape_RND_to_SP(level);
6910
6911     SaveNativeLevel_SP(filename);
6912   }
6913 }
6914
6915
6916 /* ------------------------------------------------------------------------- */
6917 /* functions for loading generic level                                       */
6918 /* ------------------------------------------------------------------------- */
6919
6920 void LoadLevelFromFileInfo(struct LevelInfo *level,
6921                            struct LevelFileInfo *level_file_info)
6922 {
6923   /* always start with reliable default values */
6924   setLevelInfoToDefaults(level);
6925
6926   switch (level_file_info->type)
6927   {
6928     case LEVEL_FILE_TYPE_RND:
6929       LoadLevelFromFileInfo_RND(level, level_file_info);
6930       break;
6931
6932     case LEVEL_FILE_TYPE_EM:
6933       LoadLevelFromFileInfo_EM(level, level_file_info);
6934       level->game_engine_type = GAME_ENGINE_TYPE_EM;
6935       break;
6936
6937     case LEVEL_FILE_TYPE_SP:
6938       LoadLevelFromFileInfo_SP(level, level_file_info);
6939       level->game_engine_type = GAME_ENGINE_TYPE_SP;
6940       break;
6941
6942     case LEVEL_FILE_TYPE_DC:
6943       LoadLevelFromFileInfo_DC(level, level_file_info);
6944       break;
6945
6946     case LEVEL_FILE_TYPE_SB:
6947       LoadLevelFromFileInfo_SB(level, level_file_info);
6948       break;
6949
6950     default:
6951       LoadLevelFromFileInfo_RND(level, level_file_info);
6952       break;
6953   }
6954
6955   /* if level file is invalid, restore level structure to default values */
6956   if (level->no_valid_file)
6957     setLevelInfoToDefaults(level);
6958
6959   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6960     level->game_engine_type = GAME_ENGINE_TYPE_RND;
6961
6962   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6963     CopyNativeLevel_Native_to_RND(level);
6964 }
6965
6966 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6967 {
6968   static struct LevelFileInfo level_file_info;
6969
6970   /* always start with reliable default values */
6971   setFileInfoToDefaults(&level_file_info);
6972
6973   level_file_info.nr = 0;                       /* unknown level number */
6974   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
6975   level_file_info.filename = filename;
6976
6977   LoadLevelFromFileInfo(level, &level_file_info);
6978 }
6979
6980 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6981 {
6982   int i, j;
6983
6984   if (leveldir_current == NULL)         /* only when dumping level */
6985     return;
6986
6987   /* all engine modifications also valid for levels which use latest engine */
6988   if (level->game_version < VERSION_IDENT(3,2,0,5))
6989   {
6990     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6991     level->score[SC_TIME_BONUS] /= 10;
6992   }
6993
6994 #if 0
6995   leveldir_current->latest_engine = TRUE;       /* !!! TEST ONLY !!! */
6996 #endif
6997
6998   if (leveldir_current->latest_engine)
6999   {
7000     /* ---------- use latest game engine ----------------------------------- */
7001
7002     /* For all levels which are forced to use the latest game engine version
7003        (normally all but user contributed, private and undefined levels), set
7004        the game engine version to the actual version; this allows for actual
7005        corrections in the game engine to take effect for existing, converted
7006        levels (from "classic" or other existing games) to make the emulation
7007        of the corresponding game more accurate, while (hopefully) not breaking
7008        existing levels created from other players. */
7009
7010     level->game_version = GAME_VERSION_ACTUAL;
7011
7012     /* Set special EM style gems behaviour: EM style gems slip down from
7013        normal, steel and growing wall. As this is a more fundamental change,
7014        it seems better to set the default behaviour to "off" (as it is more
7015        natural) and make it configurable in the level editor (as a property
7016        of gem style elements). Already existing converted levels (neither
7017        private nor contributed levels) are changed to the new behaviour. */
7018
7019     if (level->file_version < FILE_VERSION_2_0)
7020       level->em_slippery_gems = TRUE;
7021
7022     return;
7023   }
7024
7025   /* ---------- use game engine the level was created with ----------------- */
7026
7027   /* For all levels which are not forced to use the latest game engine
7028      version (normally user contributed, private and undefined levels),
7029      use the version of the game engine the levels were created for.
7030
7031      Since 2.0.1, the game engine version is now directly stored
7032      in the level file (chunk "VERS"), so there is no need anymore
7033      to set the game version from the file version (except for old,
7034      pre-2.0 levels, where the game version is still taken from the
7035      file format version used to store the level -- see above). */
7036
7037   /* player was faster than enemies in 1.0.0 and before */
7038   if (level->file_version == FILE_VERSION_1_0)
7039     for (i = 0; i < MAX_PLAYERS; i++)
7040       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7041
7042   /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
7043   if (level->game_version == VERSION_IDENT(2,0,1,0))
7044     level->em_slippery_gems = TRUE;
7045
7046   /* springs could be pushed over pits before (pre-release version) 2.2.0 */
7047   if (level->game_version < VERSION_IDENT(2,2,0,0))
7048     level->use_spring_bug = TRUE;
7049
7050   if (level->game_version < VERSION_IDENT(3,2,0,5))
7051   {
7052     /* time orb caused limited time in endless time levels before 3.2.0-5 */
7053     level->use_time_orb_bug = TRUE;
7054
7055     /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
7056     level->block_snap_field = FALSE;
7057
7058     /* extra time score was same value as time left score before 3.2.0-5 */
7059     level->extra_time_score = level->score[SC_TIME_BONUS];
7060
7061 #if 0
7062     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
7063     level->score[SC_TIME_BONUS] /= 10;
7064 #endif
7065   }
7066
7067   if (level->game_version < VERSION_IDENT(3,2,0,7))
7068   {
7069     /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
7070     level->continuous_snapping = FALSE;
7071   }
7072
7073   /* only few elements were able to actively move into acid before 3.1.0 */
7074   /* trigger settings did not exist before 3.1.0; set to default "any" */
7075   if (level->game_version < VERSION_IDENT(3,1,0,0))
7076   {
7077     /* correct "can move into acid" settings (all zero in old levels) */
7078
7079     level->can_move_into_acid_bits = 0; /* nothing can move into acid */
7080     level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
7081
7082     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7083     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7084     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7085     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7086
7087     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7088       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7089
7090     /* correct trigger settings (stored as zero == "none" in old levels) */
7091
7092     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7093     {
7094       int element = EL_CUSTOM_START + i;
7095       struct ElementInfo *ei = &element_info[element];
7096
7097       for (j = 0; j < ei->num_change_pages; j++)
7098       {
7099         struct ElementChangeInfo *change = &ei->change_page[j];
7100
7101         change->trigger_player = CH_PLAYER_ANY;
7102         change->trigger_page = CH_PAGE_ANY;
7103       }
7104     }
7105   }
7106
7107   /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
7108   {
7109     int element = EL_CUSTOM_256;
7110     struct ElementInfo *ei = &element_info[element];
7111     struct ElementChangeInfo *change = &ei->change_page[0];
7112
7113     /* This is needed to fix a problem that was caused by a bugfix in function
7114        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7115        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7116        not replace walkable elements, but instead just placed the player on it,
7117        without placing the Sokoban field under the player). Unfortunately, this
7118        breaks "Snake Bite" style levels when the snake is halfway through a door
7119        that just closes (the snake head is still alive and can be moved in this
7120        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7121        player (without Sokoban element) which then gets killed as designed). */
7122
7123     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7124          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7125         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7126       change->target_element = EL_PLAYER_1;
7127   }
7128
7129 #if 1
7130   /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
7131   if (level->game_version < VERSION_IDENT(3,2,5,0))
7132   {
7133     /* This is needed to fix a problem that was caused by a bugfix in function
7134        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7135        corrects the behaviour when a custom element changes to another custom
7136        element with a higher element number that has change actions defined.
7137        Normally, only one change per frame is allowed for custom elements.
7138        Therefore, it is checked if a custom element already changed in the
7139        current frame; if it did, subsequent changes are suppressed.
7140        Unfortunately, this is only checked for element changes, but not for
7141        change actions, which are still executed. As the function above loops
7142        through all custom elements from lower to higher, an element change
7143        resulting in a lower CE number won't be checked again, while a target
7144        element with a higher number will also be checked, and potential change
7145        actions will get executed for this CE, too (which is wrong), while
7146        further changes are ignored (which is correct). As this bugfix breaks
7147        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7148        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7149        behaviour for existing levels and tapes that make use of this bug */
7150
7151     level->use_action_after_change_bug = TRUE;
7152   }
7153 #else
7154   /* !!! THIS DOES NOT FIX "Zelda I" (GRAPHICALLY) AND "Alan's FMV" LEVELS */
7155   /* try to detect and fix "Zelda II" levels, which are broken with 3.2.5 */
7156   {
7157     int element = EL_CUSTOM_16;
7158     struct ElementInfo *ei = &element_info[element];
7159
7160     /* This is needed to fix a problem that was caused by a bugfix in function
7161        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7162        corrects the behaviour when a custom element changes to another custom
7163        element with a higher element number that has change actions defined.
7164        Normally, only one change per frame is allowed for custom elements.
7165        Therefore, it is checked if a custom element already changed in the
7166        current frame; if it did, subsequent changes are suppressed.
7167        Unfortunately, this is only checked for element changes, but not for
7168        change actions, which are still executed. As the function above loops
7169        through all custom elements from lower to higher, an element change
7170        resulting in a lower CE number won't be checked again, while a target
7171        element with a higher number will also be checked, and potential change
7172        actions will get executed for this CE, too (which is wrong), while
7173        further changes are ignored (which is correct). As this bugfix breaks
7174        Zelda II (but no other levels), allow the previous, incorrect behaviour
7175        for this outstanding level set to not break the game or existing tapes */
7176
7177     if (strncmp(leveldir_current->identifier, "zelda2", 6) == 0 ||
7178         strncmp(ei->description, "scanline - row 1", 16) == 0)
7179       level->use_action_after_change_bug = TRUE;
7180   }
7181 #endif
7182
7183   /* not centering level after relocating player was default only in 3.2.3 */
7184   if (level->game_version == VERSION_IDENT(3,2,3,0))    /* (no pre-releases) */
7185     level->shifted_relocation = TRUE;
7186
7187   /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
7188   if (level->game_version < VERSION_IDENT(3,2,6,0))
7189     level->em_explodes_by_fire = TRUE;
7190 }
7191
7192 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
7193 {
7194   int i, j, x, y;
7195
7196   /* map custom element change events that have changed in newer versions
7197      (these following values were accidentally changed in version 3.0.1)
7198      (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
7199   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7200   {
7201     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7202     {
7203       int element = EL_CUSTOM_START + i;
7204
7205       /* order of checking and copying events to be mapped is important */
7206       /* (do not change the start and end value -- they are constant) */
7207       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7208       {
7209         if (HAS_CHANGE_EVENT(element, j - 2))
7210         {
7211           SET_CHANGE_EVENT(element, j - 2, FALSE);
7212           SET_CHANGE_EVENT(element, j, TRUE);
7213         }
7214       }
7215
7216       /* order of checking and copying events to be mapped is important */
7217       /* (do not change the start and end value -- they are constant) */
7218       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7219       {
7220         if (HAS_CHANGE_EVENT(element, j - 1))
7221         {
7222           SET_CHANGE_EVENT(element, j - 1, FALSE);
7223           SET_CHANGE_EVENT(element, j, TRUE);
7224         }
7225       }
7226     }
7227   }
7228
7229   /* initialize "can_change" field for old levels with only one change page */
7230   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7231   {
7232     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7233     {
7234       int element = EL_CUSTOM_START + i;
7235
7236       if (CAN_CHANGE(element))
7237         element_info[element].change->can_change = TRUE;
7238     }
7239   }
7240
7241   /* correct custom element values (for old levels without these options) */
7242   if (level->game_version < VERSION_IDENT(3,1,1,0))
7243   {
7244     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7245     {
7246       int element = EL_CUSTOM_START + i;
7247       struct ElementInfo *ei = &element_info[element];
7248
7249       if (ei->access_direction == MV_NO_DIRECTION)
7250         ei->access_direction = MV_ALL_DIRECTIONS;
7251     }
7252   }
7253
7254   /* correct custom element values (fix invalid values for all versions) */
7255   if (1)
7256   {
7257     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7258     {
7259       int element = EL_CUSTOM_START + i;
7260       struct ElementInfo *ei = &element_info[element];
7261
7262       for (j = 0; j < ei->num_change_pages; j++)
7263       {
7264         struct ElementChangeInfo *change = &ei->change_page[j];
7265
7266         if (change->trigger_player == CH_PLAYER_NONE)
7267           change->trigger_player = CH_PLAYER_ANY;
7268
7269         if (change->trigger_side == CH_SIDE_NONE)
7270           change->trigger_side = CH_SIDE_ANY;
7271       }
7272     }
7273   }
7274
7275   /* initialize "can_explode" field for old levels which did not store this */
7276   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
7277   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7278   {
7279     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7280     {
7281       int element = EL_CUSTOM_START + i;
7282
7283       if (EXPLODES_1X1_OLD(element))
7284         element_info[element].explosion_type = EXPLODES_1X1;
7285
7286       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7287                                              EXPLODES_SMASHED(element) ||
7288                                              EXPLODES_IMPACT(element)));
7289     }
7290   }
7291
7292   /* correct previously hard-coded move delay values for maze runner style */
7293   if (level->game_version < VERSION_IDENT(3,1,1,0))
7294   {
7295     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7296     {
7297       int element = EL_CUSTOM_START + i;
7298
7299       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7300       {
7301         /* previously hard-coded and therefore ignored */
7302         element_info[element].move_delay_fixed = 9;
7303         element_info[element].move_delay_random = 0;
7304       }
7305     }
7306   }
7307
7308   /* map elements that have changed in newer versions */
7309   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7310                                                     level->game_version);
7311   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7312     for (x = 0; x < 3; x++)
7313       for (y = 0; y < 3; y++)
7314         level->yamyam_content[i].e[x][y] =
7315           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7316                                     level->game_version);
7317
7318   /* initialize element properties for level editor etc. */
7319   InitElementPropertiesEngine(level->game_version);
7320   InitElementPropertiesAfterLoading(level->game_version);
7321   InitElementPropertiesGfxElement();
7322 }
7323
7324 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
7325 {
7326   int x, y;
7327
7328   /* map elements that have changed in newer versions */
7329   for (y = 0; y < level->fieldy; y++)
7330     for (x = 0; x < level->fieldx; x++)
7331       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7332                                                      level->game_version);
7333
7334   /* copy elements to runtime playfield array */
7335   for (x = 0; x < MAX_LEV_FIELDX; x++)
7336     for (y = 0; y < MAX_LEV_FIELDY; y++)
7337       Feld[x][y] = level->field[x][y];
7338
7339   /* initialize level size variables for faster access */
7340   lev_fieldx = level->fieldx;
7341   lev_fieldy = level->fieldy;
7342
7343   /* determine border element for this level */
7344   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7345     BorderElement = EL_EMPTY;   /* (in editor, SetBorderElement() is used) */
7346   else
7347     SetBorderElement();
7348 }
7349
7350 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
7351 {
7352   struct LevelFileInfo *level_file_info = &level->file_info;
7353
7354   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7355     CopyNativeLevel_RND_to_Native(level);
7356 }
7357
7358 void LoadLevelTemplate(int nr)
7359 {
7360   char *filename;
7361
7362   setLevelFileInfo(&level_template.file_info, nr);
7363   filename = level_template.file_info.filename;
7364
7365   LoadLevelFromFileInfo(&level_template, &level_template.file_info);
7366
7367   LoadLevel_InitVersion(&level_template, filename);
7368   LoadLevel_InitElements(&level_template, filename);
7369
7370   ActivateLevelTemplate();
7371 }
7372
7373 void LoadLevel(int nr)
7374 {
7375   char *filename;
7376
7377   setLevelFileInfo(&level.file_info, nr);
7378   filename = level.file_info.filename;
7379
7380   LoadLevelFromFileInfo(&level, &level.file_info);
7381
7382   if (level.use_custom_template)
7383     LoadLevelTemplate(-1);
7384
7385   LoadLevel_InitVersion(&level, filename);
7386   LoadLevel_InitElements(&level, filename);
7387   LoadLevel_InitPlayfield(&level, filename);
7388
7389   LoadLevel_InitNativeEngines(&level, filename);
7390 }
7391
7392 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7393 {
7394   int chunk_size = 0;
7395
7396   chunk_size += putFileVersion(file, level->file_version);
7397   chunk_size += putFileVersion(file, level->game_version);
7398
7399   return chunk_size;
7400 }
7401
7402 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7403 {
7404   int chunk_size = 0;
7405
7406   chunk_size += putFile16BitBE(file, level->creation_date.year);
7407   chunk_size += putFile8Bit(file,    level->creation_date.month);
7408   chunk_size += putFile8Bit(file,    level->creation_date.day);
7409
7410   return chunk_size;
7411 }
7412
7413 #if 0
7414 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7415 {
7416   int i, x, y;
7417
7418   putFile8Bit(file, level->fieldx);
7419   putFile8Bit(file, level->fieldy);
7420
7421   putFile16BitBE(file, level->time);
7422   putFile16BitBE(file, level->gems_needed);
7423
7424   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7425     putFile8Bit(file, level->name[i]);
7426
7427   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7428     putFile8Bit(file, level->score[i]);
7429
7430   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7431     for (y = 0; y < 3; y++)
7432       for (x = 0; x < 3; x++)
7433         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7434                            level->yamyam_content[i].e[x][y]));
7435   putFile8Bit(file, level->amoeba_speed);
7436   putFile8Bit(file, level->time_magic_wall);
7437   putFile8Bit(file, level->time_wheel);
7438   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7439                      level->amoeba_content));
7440   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7441   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7442   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7443   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7444
7445   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7446
7447   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7448   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7449   putFile32BitBE(file, level->can_move_into_acid_bits);
7450   putFile8Bit(file, level->dont_collide_with_bits);
7451
7452   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7453   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7454
7455   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7456   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7457   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7458
7459   putFile8Bit(file, level->game_engine_type);
7460
7461   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7462 }
7463 #endif
7464
7465 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7466 {
7467   int chunk_size = 0;
7468   int i;
7469
7470   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7471     chunk_size += putFile8Bit(file, level->name[i]);
7472
7473   return chunk_size;
7474 }
7475
7476 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7477 {
7478   int chunk_size = 0;
7479   int i;
7480
7481   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7482     chunk_size += putFile8Bit(file, level->author[i]);
7483
7484   return chunk_size;
7485 }
7486
7487 #if 0
7488 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7489 {
7490   int chunk_size = 0;
7491   int x, y;
7492
7493   for (y = 0; y < level->fieldy; y++) 
7494     for (x = 0; x < level->fieldx; x++) 
7495       if (level->encoding_16bit_field)
7496         chunk_size += putFile16BitBE(file, level->field[x][y]);
7497       else
7498         chunk_size += putFile8Bit(file, level->field[x][y]);
7499
7500   return chunk_size;
7501 }
7502 #endif
7503
7504 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7505 {
7506   int chunk_size = 0;
7507   int x, y;
7508
7509   for (y = 0; y < level->fieldy; y++) 
7510     for (x = 0; x < level->fieldx; x++) 
7511       chunk_size += putFile16BitBE(file, level->field[x][y]);
7512
7513   return chunk_size;
7514 }
7515
7516 #if 0
7517 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7518 {
7519   int i, x, y;
7520
7521   putFile8Bit(file, EL_YAMYAM);
7522   putFile8Bit(file, level->num_yamyam_contents);
7523   putFile8Bit(file, 0);
7524   putFile8Bit(file, 0);
7525
7526   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7527     for (y = 0; y < 3; y++)
7528       for (x = 0; x < 3; x++)
7529         if (level->encoding_16bit_field)
7530           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7531         else
7532           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7533 }
7534 #endif
7535
7536 #if 0
7537 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7538 {
7539   int i, x, y;
7540   int num_contents, content_xsize, content_ysize;
7541   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7542
7543   if (element == EL_YAMYAM)
7544   {
7545     num_contents = level->num_yamyam_contents;
7546     content_xsize = 3;
7547     content_ysize = 3;
7548
7549     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7550       for (y = 0; y < 3; y++)
7551         for (x = 0; x < 3; x++)
7552           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7553   }
7554   else if (element == EL_BD_AMOEBA)
7555   {
7556     num_contents = 1;
7557     content_xsize = 1;
7558     content_ysize = 1;
7559
7560     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7561       for (y = 0; y < 3; y++)
7562         for (x = 0; x < 3; x++)
7563           content_array[i][x][y] = EL_EMPTY;
7564     content_array[0][0][0] = level->amoeba_content;
7565   }
7566   else
7567   {
7568     /* chunk header already written -- write empty chunk data */
7569     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7570
7571     Error(ERR_WARN, "cannot save content for element '%d'", element);
7572     return;
7573   }
7574
7575   putFile16BitBE(file, element);
7576   putFile8Bit(file, num_contents);
7577   putFile8Bit(file, content_xsize);
7578   putFile8Bit(file, content_ysize);
7579
7580   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7581
7582   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7583     for (y = 0; y < 3; y++)
7584       for (x = 0; x < 3; x++)
7585         putFile16BitBE(file, content_array[i][x][y]);
7586 }
7587 #endif
7588
7589 #if 0
7590 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7591 {
7592   int envelope_nr = element - EL_ENVELOPE_1;
7593   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7594   int chunk_size = 0;
7595   int i;
7596
7597   chunk_size += putFile16BitBE(file, element);
7598   chunk_size += putFile16BitBE(file, envelope_len);
7599   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7600   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7601
7602   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7603   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7604
7605   for (i = 0; i < envelope_len; i++)
7606     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7607
7608   return chunk_size;
7609 }
7610 #endif
7611
7612 #if 0
7613 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7614                            int num_changed_custom_elements)
7615 {
7616   int i, check = 0;
7617
7618   putFile16BitBE(file, num_changed_custom_elements);
7619
7620   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7621   {
7622     int element = EL_CUSTOM_START + i;
7623
7624     struct ElementInfo *ei = &element_info[element];
7625
7626     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7627     {
7628       if (check < num_changed_custom_elements)
7629       {
7630         putFile16BitBE(file, element);
7631         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7632       }
7633
7634       check++;
7635     }
7636   }
7637
7638   if (check != num_changed_custom_elements)     /* should not happen */
7639     Error(ERR_WARN, "inconsistent number of custom element properties");
7640 }
7641 #endif
7642
7643 #if 0
7644 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7645                            int num_changed_custom_elements)
7646 {
7647   int i, check = 0;
7648
7649   putFile16BitBE(file, num_changed_custom_elements);
7650
7651   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7652   {
7653     int element = EL_CUSTOM_START + i;
7654
7655     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7656     {
7657       if (check < num_changed_custom_elements)
7658       {
7659         putFile16BitBE(file, element);
7660         putFile16BitBE(file, element_info[element].change->target_element);
7661       }
7662
7663       check++;
7664     }
7665   }
7666
7667   if (check != num_changed_custom_elements)     /* should not happen */
7668     Error(ERR_WARN, "inconsistent number of custom target elements");
7669 }
7670 #endif
7671
7672 #if 0
7673 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7674                            int num_changed_custom_elements)
7675 {
7676   int i, j, x, y, check = 0;
7677
7678   putFile16BitBE(file, num_changed_custom_elements);
7679
7680   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7681   {
7682     int element = EL_CUSTOM_START + i;
7683     struct ElementInfo *ei = &element_info[element];
7684
7685     if (ei->modified_settings)
7686     {
7687       if (check < num_changed_custom_elements)
7688       {
7689         putFile16BitBE(file, element);
7690
7691         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7692           putFile8Bit(file, ei->description[j]);
7693
7694         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7695
7696         /* some free bytes for future properties and padding */
7697         WriteUnusedBytesToFile(file, 7);
7698
7699         putFile8Bit(file, ei->use_gfx_element);
7700         putFile16BitBE(file, ei->gfx_element_initial);
7701
7702         putFile8Bit(file, ei->collect_score_initial);
7703         putFile8Bit(file, ei->collect_count_initial);
7704
7705         putFile16BitBE(file, ei->push_delay_fixed);
7706         putFile16BitBE(file, ei->push_delay_random);
7707         putFile16BitBE(file, ei->move_delay_fixed);
7708         putFile16BitBE(file, ei->move_delay_random);
7709
7710         putFile16BitBE(file, ei->move_pattern);
7711         putFile8Bit(file, ei->move_direction_initial);
7712         putFile8Bit(file, ei->move_stepsize);
7713
7714         for (y = 0; y < 3; y++)
7715           for (x = 0; x < 3; x++)
7716             putFile16BitBE(file, ei->content.e[x][y]);
7717
7718         putFile32BitBE(file, ei->change->events);
7719
7720         putFile16BitBE(file, ei->change->target_element);
7721
7722         putFile16BitBE(file, ei->change->delay_fixed);
7723         putFile16BitBE(file, ei->change->delay_random);
7724         putFile16BitBE(file, ei->change->delay_frames);
7725
7726         putFile16BitBE(file, ei->change->initial_trigger_element);
7727
7728         putFile8Bit(file, ei->change->explode);
7729         putFile8Bit(file, ei->change->use_target_content);
7730         putFile8Bit(file, ei->change->only_if_complete);
7731         putFile8Bit(file, ei->change->use_random_replace);
7732
7733         putFile8Bit(file, ei->change->random_percentage);
7734         putFile8Bit(file, ei->change->replace_when);
7735
7736         for (y = 0; y < 3; y++)
7737           for (x = 0; x < 3; x++)
7738             putFile16BitBE(file, ei->change->content.e[x][y]);
7739
7740         putFile8Bit(file, ei->slippery_type);
7741
7742         /* some free bytes for future properties and padding */
7743         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7744       }
7745
7746       check++;
7747     }
7748   }
7749
7750   if (check != num_changed_custom_elements)     /* should not happen */
7751     Error(ERR_WARN, "inconsistent number of custom element properties");
7752 }
7753 #endif
7754
7755 #if 0
7756 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7757 {
7758   struct ElementInfo *ei = &element_info[element];
7759   int i, j, x, y;
7760
7761   /* ---------- custom element base property values (96 bytes) ------------- */
7762
7763   putFile16BitBE(file, element);
7764
7765   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7766     putFile8Bit(file, ei->description[i]);
7767
7768   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7769
7770   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
7771
7772   putFile8Bit(file, ei->num_change_pages);
7773
7774   putFile16BitBE(file, ei->ce_value_fixed_initial);
7775   putFile16BitBE(file, ei->ce_value_random_initial);
7776   putFile8Bit(file, ei->use_last_ce_value);
7777
7778   putFile8Bit(file, ei->use_gfx_element);
7779   putFile16BitBE(file, ei->gfx_element_initial);
7780
7781   putFile8Bit(file, ei->collect_score_initial);
7782   putFile8Bit(file, ei->collect_count_initial);
7783
7784   putFile8Bit(file, ei->drop_delay_fixed);
7785   putFile8Bit(file, ei->push_delay_fixed);
7786   putFile8Bit(file, ei->drop_delay_random);
7787   putFile8Bit(file, ei->push_delay_random);
7788   putFile16BitBE(file, ei->move_delay_fixed);
7789   putFile16BitBE(file, ei->move_delay_random);
7790
7791   /* bits 0 - 15 of "move_pattern" ... */
7792   putFile16BitBE(file, ei->move_pattern & 0xffff);
7793   putFile8Bit(file, ei->move_direction_initial);
7794   putFile8Bit(file, ei->move_stepsize);
7795
7796   putFile8Bit(file, ei->slippery_type);
7797
7798   for (y = 0; y < 3; y++)
7799     for (x = 0; x < 3; x++)
7800       putFile16BitBE(file, ei->content.e[x][y]);
7801
7802   putFile16BitBE(file, ei->move_enter_element);
7803   putFile16BitBE(file, ei->move_leave_element);
7804   putFile8Bit(file, ei->move_leave_type);
7805
7806   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7807   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7808
7809   putFile8Bit(file, ei->access_direction);
7810
7811   putFile8Bit(file, ei->explosion_delay);
7812   putFile8Bit(file, ei->ignition_delay);
7813   putFile8Bit(file, ei->explosion_type);
7814
7815   /* some free bytes for future custom property values and padding */
7816   WriteUnusedBytesToFile(file, 1);
7817
7818   /* ---------- change page property values (48 bytes) --------------------- */
7819
7820   for (i = 0; i < ei->num_change_pages; i++)
7821   {
7822     struct ElementChangeInfo *change = &ei->change_page[i];
7823     unsigned int event_bits;
7824
7825     /* bits 0 - 31 of "has_event[]" ... */
7826     event_bits = 0;
7827     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7828       if (change->has_event[j])
7829         event_bits |= (1 << j);
7830     putFile32BitBE(file, event_bits);
7831
7832     putFile16BitBE(file, change->target_element);
7833
7834     putFile16BitBE(file, change->delay_fixed);
7835     putFile16BitBE(file, change->delay_random);
7836     putFile16BitBE(file, change->delay_frames);
7837
7838     putFile16BitBE(file, change->initial_trigger_element);
7839
7840     putFile8Bit(file, change->explode);
7841     putFile8Bit(file, change->use_target_content);
7842     putFile8Bit(file, change->only_if_complete);
7843     putFile8Bit(file, change->use_random_replace);
7844
7845     putFile8Bit(file, change->random_percentage);
7846     putFile8Bit(file, change->replace_when);
7847
7848     for (y = 0; y < 3; y++)
7849       for (x = 0; x < 3; x++)
7850         putFile16BitBE(file, change->target_content.e[x][y]);
7851
7852     putFile8Bit(file, change->can_change);
7853
7854     putFile8Bit(file, change->trigger_side);
7855
7856     putFile8Bit(file, change->trigger_player);
7857     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7858                        log_2(change->trigger_page)));
7859
7860     putFile8Bit(file, change->has_action);
7861     putFile8Bit(file, change->action_type);
7862     putFile8Bit(file, change->action_mode);
7863     putFile16BitBE(file, change->action_arg);
7864
7865     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7866     event_bits = 0;
7867     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7868       if (change->has_event[j])
7869         event_bits |= (1 << (j - 32));
7870     putFile8Bit(file, event_bits);
7871   }
7872 }
7873 #endif
7874
7875 #if 0
7876 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7877 {
7878   struct ElementInfo *ei = &element_info[element];
7879   struct ElementGroupInfo *group = ei->group;
7880   int i;
7881
7882   putFile16BitBE(file, element);
7883
7884   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7885     putFile8Bit(file, ei->description[i]);
7886
7887   putFile8Bit(file, group->num_elements);
7888
7889   putFile8Bit(file, ei->use_gfx_element);
7890   putFile16BitBE(file, ei->gfx_element_initial);
7891
7892   putFile8Bit(file, group->choice_mode);
7893
7894   /* some free bytes for future values and padding */
7895   WriteUnusedBytesToFile(file, 3);
7896
7897   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7898     putFile16BitBE(file, group->element[i]);
7899 }
7900 #endif
7901
7902 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7903                                 boolean write_element)
7904 {
7905   int save_type = entry->save_type;
7906   int data_type = entry->data_type;
7907   int conf_type = entry->conf_type;
7908   int byte_mask = conf_type & CONF_MASK_BYTES;
7909   int element = entry->element;
7910   int default_value = entry->default_value;
7911   int num_bytes = 0;
7912   boolean modified = FALSE;
7913
7914   if (byte_mask != CONF_MASK_MULTI_BYTES)
7915   {
7916     void *value_ptr = entry->value;
7917     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7918                  *(int *)value_ptr);
7919
7920     /* check if any settings have been modified before saving them */
7921     if (value != default_value)
7922       modified = TRUE;
7923
7924     /* do not save if explicitly told or if unmodified default settings */
7925     if ((save_type == SAVE_CONF_NEVER) ||
7926         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7927       return 0;
7928
7929     if (write_element)
7930       num_bytes += putFile16BitBE(file, element);
7931
7932     num_bytes += putFile8Bit(file, conf_type);
7933     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7934                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7935                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7936                   0);
7937   }
7938   else if (data_type == TYPE_STRING)
7939   {
7940     char *default_string = entry->default_string;
7941     char *string = (char *)(entry->value);
7942     int string_length = strlen(string);
7943     int i;
7944
7945     /* check if any settings have been modified before saving them */
7946     if (!strEqual(string, default_string))
7947       modified = TRUE;
7948
7949     /* do not save if explicitly told or if unmodified default settings */
7950     if ((save_type == SAVE_CONF_NEVER) ||
7951         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7952       return 0;
7953
7954     if (write_element)
7955       num_bytes += putFile16BitBE(file, element);
7956
7957     num_bytes += putFile8Bit(file, conf_type);
7958     num_bytes += putFile16BitBE(file, string_length);
7959
7960     for (i = 0; i < string_length; i++)
7961       num_bytes += putFile8Bit(file, string[i]);
7962   }
7963   else if (data_type == TYPE_ELEMENT_LIST)
7964   {
7965     int *element_array = (int *)(entry->value);
7966     int num_elements = *(int *)(entry->num_entities);
7967     int i;
7968
7969     /* check if any settings have been modified before saving them */
7970     for (i = 0; i < num_elements; i++)
7971       if (element_array[i] != default_value)
7972         modified = TRUE;
7973
7974     /* do not save if explicitly told or if unmodified default settings */
7975     if ((save_type == SAVE_CONF_NEVER) ||
7976         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7977       return 0;
7978
7979     if (write_element)
7980       num_bytes += putFile16BitBE(file, element);
7981
7982     num_bytes += putFile8Bit(file, conf_type);
7983     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7984
7985     for (i = 0; i < num_elements; i++)
7986       num_bytes += putFile16BitBE(file, element_array[i]);
7987   }
7988   else if (data_type == TYPE_CONTENT_LIST)
7989   {
7990     struct Content *content = (struct Content *)(entry->value);
7991     int num_contents = *(int *)(entry->num_entities);
7992     int i, x, y;
7993
7994     /* check if any settings have been modified before saving them */
7995     for (i = 0; i < num_contents; i++)
7996       for (y = 0; y < 3; y++)
7997         for (x = 0; x < 3; x++)
7998           if (content[i].e[x][y] != default_value)
7999             modified = TRUE;
8000
8001     /* do not save if explicitly told or if unmodified default settings */
8002     if ((save_type == SAVE_CONF_NEVER) ||
8003         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8004       return 0;
8005
8006     if (write_element)
8007       num_bytes += putFile16BitBE(file, element);
8008
8009     num_bytes += putFile8Bit(file, conf_type);
8010     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8011
8012     for (i = 0; i < num_contents; i++)
8013       for (y = 0; y < 3; y++)
8014         for (x = 0; x < 3; x++)
8015           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8016   }
8017
8018   return num_bytes;
8019 }
8020
8021 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8022 {
8023   int chunk_size = 0;
8024   int i;
8025
8026   li = *level;          /* copy level data into temporary buffer */
8027
8028   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8029     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8030
8031   return chunk_size;
8032 }
8033
8034 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8035 {
8036   int chunk_size = 0;
8037   int i;
8038
8039   li = *level;          /* copy level data into temporary buffer */
8040
8041   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8042     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8043
8044   return chunk_size;
8045 }
8046
8047 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8048 {
8049   int envelope_nr = element - EL_ENVELOPE_1;
8050   int chunk_size = 0;
8051   int i;
8052
8053   chunk_size += putFile16BitBE(file, element);
8054
8055   /* copy envelope data into temporary buffer */
8056   xx_envelope = level->envelope[envelope_nr];
8057
8058   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8059     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8060
8061   return chunk_size;
8062 }
8063
8064 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8065 {
8066   struct ElementInfo *ei = &element_info[element];
8067   int chunk_size = 0;
8068   int i, j;
8069
8070   chunk_size += putFile16BitBE(file, element);
8071
8072   xx_ei = *ei;          /* copy element data into temporary buffer */
8073
8074   /* set default description string for this specific element */
8075   strcpy(xx_default_description, getDefaultElementDescription(ei));
8076
8077 #if 0
8078   /* set (fixed) number of content areas (may be wrong by broken level file) */
8079   /* (this is now directly corrected for broken level files after loading) */
8080   xx_num_contents = 1;
8081 #endif
8082
8083   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8084     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8085
8086   for (i = 0; i < ei->num_change_pages; i++)
8087   {
8088     struct ElementChangeInfo *change = &ei->change_page[i];
8089
8090     xx_current_change_page = i;
8091
8092     xx_change = *change;        /* copy change data into temporary buffer */
8093
8094     resetEventBits();
8095     setEventBitsFromEventFlags(change);
8096
8097     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8098       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8099                                          FALSE);
8100   }
8101
8102   return chunk_size;
8103 }
8104
8105 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8106 {
8107   struct ElementInfo *ei = &element_info[element];
8108   struct ElementGroupInfo *group = ei->group;
8109   int chunk_size = 0;
8110   int i;
8111
8112   chunk_size += putFile16BitBE(file, element);
8113
8114   xx_ei = *ei;          /* copy element data into temporary buffer */
8115   xx_group = *group;    /* copy group data into temporary buffer */
8116
8117   /* set default description string for this specific element */
8118   strcpy(xx_default_description, getDefaultElementDescription(ei));
8119
8120   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8121     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8122
8123   return chunk_size;
8124 }
8125
8126 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
8127 {
8128   int chunk_size;
8129   int i;
8130   FILE *file;
8131
8132   if (!(file = fopen(filename, MODE_WRITE)))
8133   {
8134     Error(ERR_WARN, "cannot save level file '%s'", filename);
8135     return;
8136   }
8137
8138   level->file_version = FILE_VERSION_ACTUAL;
8139   level->game_version = GAME_VERSION_ACTUAL;
8140
8141   level->creation_date = getCurrentDate();
8142
8143   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8144   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8145
8146   chunk_size = SaveLevel_VERS(NULL, level);
8147   putFileChunkBE(file, "VERS", chunk_size);
8148   SaveLevel_VERS(file, level);
8149
8150   chunk_size = SaveLevel_DATE(NULL, level);
8151   putFileChunkBE(file, "DATE", chunk_size);
8152   SaveLevel_DATE(file, level);
8153
8154   chunk_size = SaveLevel_NAME(NULL, level);
8155   putFileChunkBE(file, "NAME", chunk_size);
8156   SaveLevel_NAME(file, level);
8157
8158   chunk_size = SaveLevel_AUTH(NULL, level);
8159   putFileChunkBE(file, "AUTH", chunk_size);
8160   SaveLevel_AUTH(file, level);
8161
8162   chunk_size = SaveLevel_INFO(NULL, level);
8163   putFileChunkBE(file, "INFO", chunk_size);
8164   SaveLevel_INFO(file, level);
8165
8166   chunk_size = SaveLevel_BODY(NULL, level);
8167   putFileChunkBE(file, "BODY", chunk_size);
8168   SaveLevel_BODY(file, level);
8169
8170   chunk_size = SaveLevel_ELEM(NULL, level);
8171   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          /* save if changed */
8172   {
8173     putFileChunkBE(file, "ELEM", chunk_size);
8174     SaveLevel_ELEM(file, level);
8175   }
8176
8177   for (i = 0; i < NUM_ENVELOPES; i++)
8178   {
8179     int element = EL_ENVELOPE_1 + i;
8180
8181     chunk_size = SaveLevel_NOTE(NULL, level, element);
8182     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        /* save if changed */
8183     {
8184       putFileChunkBE(file, "NOTE", chunk_size);
8185       SaveLevel_NOTE(file, level, element);
8186     }
8187   }
8188
8189   /* if not using template level, check for non-default custom/group elements */
8190   if (!level->use_custom_template)
8191   {
8192     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8193     {
8194       int element = EL_CUSTOM_START + i;
8195
8196       chunk_size = SaveLevel_CUSX(NULL, level, element);
8197       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      /* save if changed */
8198       {
8199         putFileChunkBE(file, "CUSX", chunk_size);
8200         SaveLevel_CUSX(file, level, element);
8201       }
8202     }
8203
8204     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8205     {
8206       int element = EL_GROUP_START + i;
8207
8208       chunk_size = SaveLevel_GRPX(NULL, level, element);
8209       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      /* save if changed */
8210       {
8211         putFileChunkBE(file, "GRPX", chunk_size);
8212         SaveLevel_GRPX(file, level, element);
8213       }
8214     }
8215   }
8216
8217   fclose(file);
8218
8219   SetFilePermissions(filename, PERMS_PRIVATE);
8220 }
8221
8222 void SaveLevel(int nr)
8223 {
8224   char *filename = getDefaultLevelFilename(nr);
8225
8226   SaveLevelFromFilename(&level, filename);
8227 }
8228
8229 void SaveLevelTemplate()
8230 {
8231   char *filename = getDefaultLevelFilename(-1);
8232
8233   SaveLevelFromFilename(&level, filename);
8234 }
8235
8236 boolean SaveLevelChecked(int nr)
8237 {
8238   char *filename = getDefaultLevelFilename(nr);
8239   boolean new_level = !fileExists(filename);
8240   boolean level_saved = FALSE;
8241
8242   if (new_level || Request("Save this level and kill the old ?", REQ_ASK))
8243   {
8244     SaveLevel(nr);
8245
8246     if (new_level)
8247       Request("Level saved !", REQ_CONFIRM);
8248
8249     level_saved = TRUE;
8250   }
8251
8252   return level_saved;
8253 }
8254
8255 void DumpLevel(struct LevelInfo *level)
8256 {
8257   if (level->no_valid_file)
8258   {
8259     Error(ERR_WARN, "cannot dump -- no valid level file found");
8260
8261     return;
8262   }
8263
8264   printf_line("-", 79);
8265   printf("Level xxx (file version %08d, game version %08d)\n",
8266          level->file_version, level->game_version);
8267   printf_line("-", 79);
8268
8269   printf("Level author: '%s'\n", level->author);
8270   printf("Level title:  '%s'\n", level->name);
8271   printf("\n");
8272   printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8273   printf("\n");
8274   printf("Level time:  %d seconds\n", level->time);
8275   printf("Gems needed: %d\n", level->gems_needed);
8276   printf("\n");
8277   printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
8278   printf("Time for wheel:      %d seconds\n", level->time_wheel);
8279   printf("Time for light:      %d seconds\n", level->time_light);
8280   printf("Time for timegate:   %d seconds\n", level->time_timegate);
8281   printf("\n");
8282   printf("Amoeba speed: %d\n", level->amoeba_speed);
8283   printf("\n");
8284
8285   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8286   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8287   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8288   printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8289   printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8290
8291   printf_line("-", 79);
8292 }
8293
8294
8295 /* ========================================================================= */
8296 /* tape file functions                                                       */
8297 /* ========================================================================= */
8298
8299 static void setTapeInfoToDefaults()
8300 {
8301   int i;
8302
8303   /* always start with reliable default values (empty tape) */
8304   TapeErase();
8305
8306   /* default values (also for pre-1.2 tapes) with only the first player */
8307   tape.player_participates[0] = TRUE;
8308   for (i = 1; i < MAX_PLAYERS; i++)
8309     tape.player_participates[i] = FALSE;
8310
8311   /* at least one (default: the first) player participates in every tape */
8312   tape.num_participating_players = 1;
8313
8314   tape.level_nr = level_nr;
8315   tape.counter = 0;
8316   tape.changed = FALSE;
8317
8318   tape.recording = FALSE;
8319   tape.playing = FALSE;
8320   tape.pausing = FALSE;
8321
8322   tape.no_valid_file = FALSE;
8323 }
8324
8325 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
8326 {
8327   tape->file_version = getFileVersion(file);
8328   tape->game_version = getFileVersion(file);
8329
8330   return chunk_size;
8331 }
8332
8333 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
8334 {
8335   int i;
8336
8337   tape->random_seed = getFile32BitBE(file);
8338   tape->date        = getFile32BitBE(file);
8339   tape->length      = getFile32BitBE(file);
8340
8341   /* read header fields that are new since version 1.2 */
8342   if (tape->file_version >= FILE_VERSION_1_2)
8343   {
8344     byte store_participating_players = getFile8Bit(file);
8345     int engine_version;
8346
8347     /* since version 1.2, tapes store which players participate in the tape */
8348     tape->num_participating_players = 0;
8349     for (i = 0; i < MAX_PLAYERS; i++)
8350     {
8351       tape->player_participates[i] = FALSE;
8352
8353       if (store_participating_players & (1 << i))
8354       {
8355         tape->player_participates[i] = TRUE;
8356         tape->num_participating_players++;
8357       }
8358     }
8359
8360     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
8361
8362     engine_version = getFileVersion(file);
8363     if (engine_version > 0)
8364       tape->engine_version = engine_version;
8365     else
8366       tape->engine_version = tape->game_version;
8367   }
8368
8369   return chunk_size;
8370 }
8371
8372 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
8373 {
8374   int level_identifier_size;
8375   int i;
8376
8377   level_identifier_size = getFile16BitBE(file);
8378
8379   tape->level_identifier =
8380     checked_realloc(tape->level_identifier, level_identifier_size);
8381
8382   for (i = 0; i < level_identifier_size; i++)
8383     tape->level_identifier[i] = getFile8Bit(file);
8384
8385   tape->level_nr = getFile16BitBE(file);
8386
8387   chunk_size = 2 + level_identifier_size + 2;
8388
8389   return chunk_size;
8390 }
8391
8392 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
8393 {
8394   int i, j;
8395   int chunk_size_expected =
8396     (tape->num_participating_players + 1) * tape->length;
8397
8398   if (chunk_size_expected != chunk_size)
8399   {
8400     ReadUnusedBytesFromFile(file, chunk_size);
8401     return chunk_size_expected;
8402   }
8403
8404   for (i = 0; i < tape->length; i++)
8405   {
8406     if (i >= MAX_TAPE_LEN)
8407       break;
8408
8409     for (j = 0; j < MAX_PLAYERS; j++)
8410     {
8411       tape->pos[i].action[j] = MV_NONE;
8412
8413       if (tape->player_participates[j])
8414         tape->pos[i].action[j] = getFile8Bit(file);
8415     }
8416
8417     tape->pos[i].delay = getFile8Bit(file);
8418
8419     if (tape->file_version == FILE_VERSION_1_0)
8420     {
8421       /* eliminate possible diagonal moves in old tapes */
8422       /* this is only for backward compatibility */
8423
8424       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8425       byte action = tape->pos[i].action[0];
8426       int k, num_moves = 0;
8427
8428       for (k = 0; k<4; k++)
8429       {
8430         if (action & joy_dir[k])
8431         {
8432           tape->pos[i + num_moves].action[0] = joy_dir[k];
8433           if (num_moves > 0)
8434             tape->pos[i + num_moves].delay = 0;
8435           num_moves++;
8436         }
8437       }
8438
8439       if (num_moves > 1)
8440       {
8441         num_moves--;
8442         i += num_moves;
8443         tape->length += num_moves;
8444       }
8445     }
8446     else if (tape->file_version < FILE_VERSION_2_0)
8447     {
8448       /* convert pre-2.0 tapes to new tape format */
8449
8450       if (tape->pos[i].delay > 1)
8451       {
8452         /* action part */
8453         tape->pos[i + 1] = tape->pos[i];
8454         tape->pos[i + 1].delay = 1;
8455
8456         /* delay part */
8457         for (j = 0; j < MAX_PLAYERS; j++)
8458           tape->pos[i].action[j] = MV_NONE;
8459         tape->pos[i].delay--;
8460
8461         i++;
8462         tape->length++;
8463       }
8464     }
8465
8466     if (feof(file))
8467       break;
8468   }
8469
8470   if (i != tape->length)
8471     chunk_size = (tape->num_participating_players + 1) * i;
8472
8473   return chunk_size;
8474 }
8475
8476 void LoadTapeFromFilename(char *filename)
8477 {
8478   char cookie[MAX_LINE_LEN];
8479   char chunk_name[CHUNK_ID_LEN + 1];
8480   FILE *file;
8481   int chunk_size;
8482
8483   /* always start with reliable default values */
8484   setTapeInfoToDefaults();
8485
8486   if (!(file = fopen(filename, MODE_READ)))
8487   {
8488     tape.no_valid_file = TRUE;
8489
8490     return;
8491   }
8492
8493   getFileChunkBE(file, chunk_name, NULL);
8494   if (strEqual(chunk_name, "RND1"))
8495   {
8496     getFile32BitBE(file);               /* not used */
8497
8498     getFileChunkBE(file, chunk_name, NULL);
8499     if (!strEqual(chunk_name, "TAPE"))
8500     {
8501       tape.no_valid_file = TRUE;
8502
8503       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
8504       fclose(file);
8505       return;
8506     }
8507   }
8508   else  /* check for pre-2.0 file format with cookie string */
8509   {
8510     strcpy(cookie, chunk_name);
8511     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
8512     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8513       cookie[strlen(cookie) - 1] = '\0';
8514
8515     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8516     {
8517       tape.no_valid_file = TRUE;
8518
8519       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
8520       fclose(file);
8521       return;
8522     }
8523
8524     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8525     {
8526       tape.no_valid_file = TRUE;
8527
8528       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
8529       fclose(file);
8530       return;
8531     }
8532
8533     /* pre-2.0 tape files have no game version, so use file version here */
8534     tape.game_version = tape.file_version;
8535   }
8536
8537   if (tape.file_version < FILE_VERSION_1_2)
8538   {
8539     /* tape files from versions before 1.2.0 without chunk structure */
8540     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8541     LoadTape_BODY(file, 2 * tape.length,      &tape);
8542   }
8543   else
8544   {
8545     static struct
8546     {
8547       char *name;
8548       int size;
8549       int (*loader)(FILE *, int, struct TapeInfo *);
8550     }
8551     chunk_info[] =
8552     {
8553       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
8554       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
8555       { "INFO", -1,                     LoadTape_INFO },
8556       { "BODY", -1,                     LoadTape_BODY },
8557       {  NULL,  0,                      NULL }
8558     };
8559
8560     while (getFileChunkBE(file, chunk_name, &chunk_size))
8561     {
8562       int i = 0;
8563
8564       while (chunk_info[i].name != NULL &&
8565              !strEqual(chunk_name, chunk_info[i].name))
8566         i++;
8567
8568       if (chunk_info[i].name == NULL)
8569       {
8570         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
8571               chunk_name, filename);
8572         ReadUnusedBytesFromFile(file, chunk_size);
8573       }
8574       else if (chunk_info[i].size != -1 &&
8575                chunk_info[i].size != chunk_size)
8576       {
8577         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8578               chunk_size, chunk_name, filename);
8579         ReadUnusedBytesFromFile(file, chunk_size);
8580       }
8581       else
8582       {
8583         /* call function to load this tape chunk */
8584         int chunk_size_expected =
8585           (chunk_info[i].loader)(file, chunk_size, &tape);
8586
8587         /* the size of some chunks cannot be checked before reading other
8588            chunks first (like "HEAD" and "BODY") that contain some header
8589            information, so check them here */
8590         if (chunk_size_expected != chunk_size)
8591         {
8592           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8593                 chunk_size, chunk_name, filename);
8594         }
8595       }
8596     }
8597   }
8598
8599   fclose(file);
8600
8601   tape.length_seconds = GetTapeLength();
8602
8603 #if 0
8604   printf("::: tape file version: %d\n", tape.file_version);
8605   printf("::: tape game version: %d\n", tape.game_version);
8606   printf("::: tape engine version: %d\n", tape.engine_version);
8607 #endif
8608 }
8609
8610 void LoadTape(int nr)
8611 {
8612   char *filename = getTapeFilename(nr);
8613
8614   LoadTapeFromFilename(filename);
8615 }
8616
8617 void LoadSolutionTape(int nr)
8618 {
8619   char *filename = getSolutionTapeFilename(nr);
8620
8621   LoadTapeFromFilename(filename);
8622
8623 #if 1
8624   if (TAPE_IS_EMPTY(tape) &&
8625       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8626       level.native_sp_level->demo.is_available)
8627     CopyNativeTape_SP_to_RND(&level);
8628 #endif
8629 }
8630
8631 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8632 {
8633   putFileVersion(file, tape->file_version);
8634   putFileVersion(file, tape->game_version);
8635 }
8636
8637 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8638 {
8639   int i;
8640   byte store_participating_players = 0;
8641
8642   /* set bits for participating players for compact storage */
8643   for (i = 0; i < MAX_PLAYERS; i++)
8644     if (tape->player_participates[i])
8645       store_participating_players |= (1 << i);
8646
8647   putFile32BitBE(file, tape->random_seed);
8648   putFile32BitBE(file, tape->date);
8649   putFile32BitBE(file, tape->length);
8650
8651   putFile8Bit(file, store_participating_players);
8652
8653   /* unused bytes not at the end here for 4-byte alignment of engine_version */
8654   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8655
8656   putFileVersion(file, tape->engine_version);
8657 }
8658
8659 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8660 {
8661   int level_identifier_size = strlen(tape->level_identifier) + 1;
8662   int i;
8663
8664   putFile16BitBE(file, level_identifier_size);
8665
8666   for (i = 0; i < level_identifier_size; i++)
8667     putFile8Bit(file, tape->level_identifier[i]);
8668
8669   putFile16BitBE(file, tape->level_nr);
8670 }
8671
8672 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8673 {
8674   int i, j;
8675
8676   for (i = 0; i < tape->length; i++)
8677   {
8678     for (j = 0; j < MAX_PLAYERS; j++)
8679       if (tape->player_participates[j])
8680         putFile8Bit(file, tape->pos[i].action[j]);
8681
8682     putFile8Bit(file, tape->pos[i].delay);
8683   }
8684 }
8685
8686 void SaveTape(int nr)
8687 {
8688   char *filename = getTapeFilename(nr);
8689   FILE *file;
8690 #if 0
8691   boolean new_tape = TRUE;
8692 #endif
8693   int num_participating_players = 0;
8694   int info_chunk_size;
8695   int body_chunk_size;
8696   int i;
8697
8698   InitTapeDirectory(leveldir_current->subdir);
8699
8700 #if 0
8701   /* if a tape still exists, ask to overwrite it */
8702   if (fileExists(filename))
8703   {
8704     new_tape = FALSE;
8705     if (!Request("Replace old tape ?", REQ_ASK))
8706       return;
8707   }
8708 #endif
8709
8710   if (!(file = fopen(filename, MODE_WRITE)))
8711   {
8712     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8713     return;
8714   }
8715
8716   tape.file_version = FILE_VERSION_ACTUAL;
8717   tape.game_version = GAME_VERSION_ACTUAL;
8718
8719   /* count number of participating players  */
8720   for (i = 0; i < MAX_PLAYERS; i++)
8721     if (tape.player_participates[i])
8722       num_participating_players++;
8723
8724   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8725   body_chunk_size = (num_participating_players + 1) * tape.length;
8726
8727   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8728   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8729
8730   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8731   SaveTape_VERS(file, &tape);
8732
8733   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8734   SaveTape_HEAD(file, &tape);
8735
8736   putFileChunkBE(file, "INFO", info_chunk_size);
8737   SaveTape_INFO(file, &tape);
8738
8739   putFileChunkBE(file, "BODY", body_chunk_size);
8740   SaveTape_BODY(file, &tape);
8741
8742   fclose(file);
8743
8744   SetFilePermissions(filename, PERMS_PRIVATE);
8745
8746   tape.changed = FALSE;
8747
8748 #if 0
8749   if (new_tape)
8750     Request("Tape saved !", REQ_CONFIRM);
8751 #endif
8752 }
8753
8754 boolean SaveTapeChecked(int nr)
8755 {
8756   char *filename = getTapeFilename(nr);
8757   boolean new_tape = !fileExists(filename);
8758   boolean tape_saved = FALSE;
8759
8760   if (new_tape || Request("Replace old tape ?", REQ_ASK))
8761   {
8762     SaveTape(nr);
8763
8764     if (new_tape)
8765       Request("Tape saved !", REQ_CONFIRM);
8766
8767     tape_saved = TRUE;
8768   }
8769
8770   return tape_saved;
8771 }
8772
8773 void DumpTape(struct TapeInfo *tape)
8774 {
8775   int tape_frame_counter;
8776   int i, j;
8777
8778   if (tape->no_valid_file)
8779   {
8780     Error(ERR_WARN, "cannot dump -- no valid tape file found");
8781
8782     return;
8783   }
8784
8785   printf_line("-", 79);
8786   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
8787          tape->level_nr, tape->file_version, tape->game_version);
8788   printf("                  (effective engine version %08d)\n",
8789          tape->engine_version);
8790   printf("Level series identifier: '%s'\n", tape->level_identifier);
8791   printf_line("-", 79);
8792
8793   tape_frame_counter = 0;
8794
8795   for (i = 0; i < tape->length; i++)
8796   {
8797     if (i >= MAX_TAPE_LEN)
8798       break;
8799
8800     printf("%04d: ", i);
8801
8802     for (j = 0; j < MAX_PLAYERS; j++)
8803     {
8804       if (tape->player_participates[j])
8805       {
8806         int action = tape->pos[i].action[j];
8807
8808         printf("%d:%02x ", j, action);
8809         printf("[%c%c%c%c|%c%c] - ",
8810                (action & JOY_LEFT ? '<' : ' '),
8811                (action & JOY_RIGHT ? '>' : ' '),
8812                (action & JOY_UP ? '^' : ' '),
8813                (action & JOY_DOWN ? 'v' : ' '),
8814                (action & JOY_BUTTON_1 ? '1' : ' '),
8815                (action & JOY_BUTTON_2 ? '2' : ' '));
8816       }
8817     }
8818
8819     printf("(%03d) ", tape->pos[i].delay);
8820     printf("[%05d]\n", tape_frame_counter);
8821
8822     tape_frame_counter += tape->pos[i].delay;
8823   }
8824
8825   printf_line("-", 79);
8826 }
8827
8828
8829 /* ========================================================================= */
8830 /* score file functions                                                      */
8831 /* ========================================================================= */
8832
8833 void LoadScore(int nr)
8834 {
8835   int i;
8836   char *filename = getScoreFilename(nr);
8837   char cookie[MAX_LINE_LEN];
8838   char line[MAX_LINE_LEN];
8839   char *line_ptr;
8840   FILE *file;
8841
8842   /* always start with reliable default values */
8843   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8844   {
8845     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8846     highscore[i].Score = 0;
8847   }
8848
8849   if (!(file = fopen(filename, MODE_READ)))
8850     return;
8851
8852   /* check file identifier */
8853   fgets(cookie, MAX_LINE_LEN, file);
8854   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8855     cookie[strlen(cookie) - 1] = '\0';
8856
8857   if (!checkCookieString(cookie, SCORE_COOKIE))
8858   {
8859     Error(ERR_WARN, "unknown format of score file '%s'", filename);
8860     fclose(file);
8861     return;
8862   }
8863
8864   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8865   {
8866     fscanf(file, "%d", &highscore[i].Score);
8867     fgets(line, MAX_LINE_LEN, file);
8868
8869     if (line[strlen(line) - 1] == '\n')
8870       line[strlen(line) - 1] = '\0';
8871
8872     for (line_ptr = line; *line_ptr; line_ptr++)
8873     {
8874       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8875       {
8876         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8877         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8878         break;
8879       }
8880     }
8881   }
8882
8883   fclose(file);
8884 }
8885
8886 void SaveScore(int nr)
8887 {
8888   int i;
8889   char *filename = getScoreFilename(nr);
8890   FILE *file;
8891
8892   InitScoreDirectory(leveldir_current->subdir);
8893
8894   if (!(file = fopen(filename, MODE_WRITE)))
8895   {
8896     Error(ERR_WARN, "cannot save score for level %d", nr);
8897     return;
8898   }
8899
8900   fprintf(file, "%s\n\n", SCORE_COOKIE);
8901
8902   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8903     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8904
8905   fclose(file);
8906
8907   SetFilePermissions(filename, PERMS_PUBLIC);
8908 }
8909
8910
8911 /* ========================================================================= */
8912 /* setup file functions                                                      */
8913 /* ========================================================================= */
8914
8915 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8916
8917 /* global setup */
8918 #define SETUP_TOKEN_PLAYER_NAME                 0
8919 #define SETUP_TOKEN_SOUND                       1
8920 #define SETUP_TOKEN_SOUND_LOOPS                 2
8921 #define SETUP_TOKEN_SOUND_MUSIC                 3
8922 #define SETUP_TOKEN_SOUND_SIMPLE                4
8923 #define SETUP_TOKEN_TOONS                       5
8924 #define SETUP_TOKEN_SCROLL_DELAY                6
8925 #define SETUP_TOKEN_SCROLL_DELAY_VALUE          7
8926 #define SETUP_TOKEN_SOFT_SCROLLING              8
8927 #define SETUP_TOKEN_FADE_SCREENS                9
8928 #define SETUP_TOKEN_AUTORECORD                  10
8929 #define SETUP_TOKEN_SHOW_TITLESCREEN            11
8930 #define SETUP_TOKEN_QUICK_DOORS                 12
8931 #define SETUP_TOKEN_TEAM_MODE                   13
8932 #define SETUP_TOKEN_HANDICAP                    14
8933 #define SETUP_TOKEN_SKIP_LEVELS                 15
8934 #define SETUP_TOKEN_TIME_LIMIT                  16
8935 #define SETUP_TOKEN_FULLSCREEN                  17
8936 #define SETUP_TOKEN_FULLSCREEN_MODE             18
8937 #define SETUP_TOKEN_ASK_ON_ESCAPE               19
8938 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        20
8939 #define SETUP_TOKEN_QUICK_SWITCH                21
8940 #define SETUP_TOKEN_INPUT_ON_FOCUS              22
8941 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         23
8942 #define SETUP_TOKEN_GAME_FRAME_DELAY            24
8943 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS     25
8944 #define SETUP_TOKEN_GRAPHICS_SET                26
8945 #define SETUP_TOKEN_SOUNDS_SET                  27
8946 #define SETUP_TOKEN_MUSIC_SET                   28
8947 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     29
8948 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       30
8949 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        31
8950
8951 #define NUM_GLOBAL_SETUP_TOKENS                 32
8952
8953 /* editor setup */
8954 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
8955 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
8956 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB 2
8957 #define SETUP_TOKEN_EDITOR_EL_MORE              3
8958 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           4
8959 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          5
8960 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     6
8961 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    7
8962 #define SETUP_TOKEN_EDITOR_EL_CHARS             8
8963 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS       9
8964 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            10
8965 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         11
8966 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      12
8967 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           13
8968 #define SETUP_TOKEN_EDITOR_EL_BY_GAME           14
8969 #define SETUP_TOKEN_EDITOR_EL_BY_TYPE           15
8970 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   16
8971
8972 #define NUM_EDITOR_SETUP_TOKENS                 17
8973
8974 /* editor cascade setup */
8975 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
8976 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
8977 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
8978 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
8979 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
8980 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
8981 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
8982 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
8983 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
8984 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT    9
8985 #define SETUP_TOKEN_EDITOR_CASCADE_CE           10
8986 #define SETUP_TOKEN_EDITOR_CASCADE_GE           11
8987 #define SETUP_TOKEN_EDITOR_CASCADE_REF          12
8988 #define SETUP_TOKEN_EDITOR_CASCADE_USER         13
8989 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      14
8990
8991 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         15
8992
8993 /* shortcut setup */
8994 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
8995 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
8996 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
8997 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
8998 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
8999 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
9000 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
9001 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
9002
9003 #define NUM_SHORTCUT_SETUP_TOKENS               8
9004
9005 /* player setup */
9006 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
9007 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
9008 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
9009 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
9010 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
9011 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
9012 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
9013 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
9014 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
9015 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
9016 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
9017 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
9018 #define SETUP_TOKEN_PLAYER_KEY_UP               12
9019 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
9020 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
9021 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
9022
9023 #define NUM_PLAYER_SETUP_TOKENS                 16
9024
9025 /* system setup */
9026 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER      0
9027 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      1
9028 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  2
9029
9030 #define NUM_SYSTEM_SETUP_TOKENS                 3
9031
9032 /* options setup */
9033 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
9034
9035 #define NUM_OPTIONS_SETUP_TOKENS                1
9036
9037
9038 static struct SetupInfo si;
9039 static struct SetupEditorInfo sei;
9040 static struct SetupEditorCascadeInfo seci;
9041 static struct SetupShortcutInfo ssi;
9042 static struct SetupInputInfo sii;
9043 static struct SetupSystemInfo syi;
9044 static struct OptionInfo soi;
9045
9046 static struct TokenInfo global_setup_tokens[] =
9047 {
9048   { TYPE_STRING, &si.player_name,             "player_name"             },
9049   { TYPE_SWITCH, &si.sound,                   "sound"                   },
9050   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
9051   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
9052   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
9053   { TYPE_SWITCH, &si.toons,                   "toons"                   },
9054   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
9055   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
9056   { TYPE_SWITCH, &si.soft_scrolling,          "soft_scrolling"          },
9057   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
9058   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
9059   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
9060   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
9061   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
9062   { TYPE_SWITCH, &si.handicap,                "handicap"                },
9063   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
9064   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
9065   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
9066   { TYPE_STRING, &si.fullscreen_mode,         "fullscreen_mode"         },
9067   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
9068   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
9069   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
9070   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
9071   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
9072   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
9073   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
9074   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
9075   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
9076   { TYPE_STRING, &si.music_set,               "music_set"               },
9077   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
9078   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
9079   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
9080 };
9081
9082 static boolean not_used = FALSE;
9083 static struct TokenInfo editor_setup_tokens[] =
9084 {
9085 #if 1
9086   { TYPE_SWITCH, &not_used,             "editor.el_boulderdash"         },
9087   { TYPE_SWITCH, &not_used,             "editor.el_emerald_mine"        },
9088   { TYPE_SWITCH, &not_used,             "editor.el_emerald_mine_club"   },
9089   { TYPE_SWITCH, &not_used,             "editor.el_more"                },
9090   { TYPE_SWITCH, &not_used,             "editor.el_sokoban"             },
9091   { TYPE_SWITCH, &not_used,             "editor.el_supaplex"            },
9092   { TYPE_SWITCH, &not_used,             "editor.el_diamond_caves"       },
9093   { TYPE_SWITCH, &not_used,             "editor.el_dx_boulderdash"      },
9094 #else
9095   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
9096   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
9097   { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
9098   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
9099   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
9100   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
9101   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
9102   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
9103 #endif
9104   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
9105   { TYPE_SWITCH, &sei.el_steel_chars,   "editor.el_steel_chars"         },
9106   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
9107 #if 1
9108   { TYPE_SWITCH, &not_used,             "editor.el_headlines"           },
9109 #else
9110   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
9111 #endif
9112   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
9113   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
9114   { TYPE_SWITCH, &sei.el_by_game,       "editor.el_by_game"             },
9115   { TYPE_SWITCH, &sei.el_by_type,       "editor.el_by_type"             },
9116   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
9117 };
9118
9119 static struct TokenInfo editor_cascade_setup_tokens[] =
9120 {
9121   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
9122   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
9123   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
9124   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
9125   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
9126   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
9127   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
9128   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
9129   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
9130   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
9131   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
9132   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
9133   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
9134   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
9135   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
9136 };
9137
9138 static struct TokenInfo shortcut_setup_tokens[] =
9139 {
9140   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
9141   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
9142   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
9143   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
9144   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
9145   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
9146   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
9147   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
9148 };
9149
9150 static struct TokenInfo player_setup_tokens[] =
9151 {
9152   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
9153   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
9154   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
9155   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
9156   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
9157   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
9158   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
9159   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
9160   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
9161   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
9162   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
9163   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
9164   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
9165   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
9166   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
9167   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
9168 };
9169
9170 static struct TokenInfo system_setup_tokens[] =
9171 {
9172   { TYPE_STRING,  &syi.sdl_videodriver, "system.sdl_videodriver"        },
9173   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
9174   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
9175 };
9176
9177 static struct TokenInfo options_setup_tokens[] =
9178 {
9179   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
9180 };
9181
9182 static char *get_corrected_login_name(char *login_name)
9183 {
9184   /* needed because player name must be a fixed length string */
9185   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
9186
9187   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
9188   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
9189
9190   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
9191     if (strchr(login_name_new, ' '))
9192       *strchr(login_name_new, ' ') = '\0';
9193
9194   return login_name_new;
9195 }
9196
9197 static void setSetupInfoToDefaults(struct SetupInfo *si)
9198 {
9199   int i;
9200
9201   si->player_name = get_corrected_login_name(getLoginName());
9202
9203   si->sound = TRUE;
9204   si->sound_loops = TRUE;
9205   si->sound_music = TRUE;
9206   si->sound_simple = TRUE;
9207   si->toons = TRUE;
9208   si->scroll_delay = TRUE;
9209   si->scroll_delay_value = STD_SCROLL_DELAY;
9210   si->soft_scrolling = TRUE;
9211   si->fade_screens = TRUE;
9212   si->autorecord = TRUE;
9213   si->show_titlescreen = TRUE;
9214   si->quick_doors = FALSE;
9215   si->team_mode = FALSE;
9216   si->handicap = TRUE;
9217   si->skip_levels = TRUE;
9218   si->time_limit = TRUE;
9219   si->fullscreen = FALSE;
9220   si->fullscreen_mode = getStringCopy(DEFAULT_FULLSCREEN_MODE);
9221   si->ask_on_escape = TRUE;
9222   si->ask_on_escape_editor = TRUE;
9223   si->quick_switch = FALSE;
9224   si->input_on_focus = FALSE;
9225   si->prefer_aga_graphics = TRUE;
9226   si->game_frame_delay = GAME_FRAME_DELAY;
9227   si->sp_show_border_elements = FALSE;
9228
9229   si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
9230   si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
9231   si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
9232   si->override_level_graphics = FALSE;
9233   si->override_level_sounds = FALSE;
9234   si->override_level_music = FALSE;
9235
9236   si->editor.el_boulderdash             = TRUE;
9237   si->editor.el_emerald_mine            = TRUE;
9238   si->editor.el_emerald_mine_club       = TRUE;
9239   si->editor.el_more                    = TRUE;
9240   si->editor.el_sokoban                 = TRUE;
9241   si->editor.el_supaplex                = TRUE;
9242   si->editor.el_diamond_caves           = TRUE;
9243   si->editor.el_dx_boulderdash          = TRUE;
9244   si->editor.el_chars                   = TRUE;
9245   si->editor.el_steel_chars             = TRUE;
9246   si->editor.el_custom                  = TRUE;
9247
9248   si->editor.el_headlines = TRUE;
9249   si->editor.el_user_defined = FALSE;
9250   si->editor.el_dynamic = TRUE;
9251
9252   si->editor.show_element_token = FALSE;
9253
9254   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
9255   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
9256   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
9257
9258   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
9259   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
9260   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
9261   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
9262   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9263
9264   for (i = 0; i < MAX_PLAYERS; i++)
9265   {
9266     si->input[i].use_joystick = FALSE;
9267     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9268     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
9269     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9270     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
9271     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
9272     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9273     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
9274     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
9275     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
9276     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
9277     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9278     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
9279     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
9280     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
9281     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
9282   }
9283
9284   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9285   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9286   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9287
9288   si->options.verbose = FALSE;
9289
9290 #if defined(CREATE_SPECIAL_EDITION_RND_JUE)
9291   si->handicap = FALSE;
9292   si->fullscreen = TRUE;
9293   si->override_level_graphics = AUTO;
9294   si->override_level_sounds = AUTO;
9295   si->override_level_music = AUTO;
9296 #endif
9297 }
9298
9299 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9300 {
9301   si->editor_cascade.el_bd              = TRUE;
9302   si->editor_cascade.el_em              = TRUE;
9303   si->editor_cascade.el_emc             = TRUE;
9304   si->editor_cascade.el_rnd             = TRUE;
9305   si->editor_cascade.el_sb              = TRUE;
9306   si->editor_cascade.el_sp              = TRUE;
9307   si->editor_cascade.el_dc              = TRUE;
9308   si->editor_cascade.el_dx              = TRUE;
9309
9310   si->editor_cascade.el_chars           = FALSE;
9311   si->editor_cascade.el_steel_chars     = FALSE;
9312   si->editor_cascade.el_ce              = FALSE;
9313   si->editor_cascade.el_ge              = FALSE;
9314   si->editor_cascade.el_ref             = FALSE;
9315   si->editor_cascade.el_user            = FALSE;
9316   si->editor_cascade.el_dynamic         = FALSE;
9317 }
9318
9319 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9320 {
9321   int i, pnr;
9322
9323   if (!setup_file_hash)
9324     return;
9325
9326   /* global setup */
9327   si = setup;
9328   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9329     setSetupInfo(global_setup_tokens, i,
9330                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
9331   setup = si;
9332
9333   /* editor setup */
9334   sei = setup.editor;
9335   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9336     setSetupInfo(editor_setup_tokens, i,
9337                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
9338   setup.editor = sei;
9339
9340   /* shortcut setup */
9341   ssi = setup.shortcut;
9342   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9343     setSetupInfo(shortcut_setup_tokens, i,
9344                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
9345   setup.shortcut = ssi;
9346
9347   /* player setup */
9348   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9349   {
9350     char prefix[30];
9351
9352     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9353
9354     sii = setup.input[pnr];
9355     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9356     {
9357       char full_token[100];
9358
9359       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9360       setSetupInfo(player_setup_tokens, i,
9361                    getHashEntry(setup_file_hash, full_token));
9362     }
9363     setup.input[pnr] = sii;
9364   }
9365
9366   /* system setup */
9367   syi = setup.system;
9368   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9369     setSetupInfo(system_setup_tokens, i,
9370                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
9371   setup.system = syi;
9372
9373   /* options setup */
9374   soi = setup.options;
9375   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9376     setSetupInfo(options_setup_tokens, i,
9377                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
9378   setup.options = soi;
9379 }
9380
9381 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9382 {
9383   int i;
9384
9385   if (!setup_file_hash)
9386     return;
9387
9388   /* editor cascade setup */
9389   seci = setup.editor_cascade;
9390   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9391     setSetupInfo(editor_cascade_setup_tokens, i,
9392                  getHashEntry(setup_file_hash,
9393                               editor_cascade_setup_tokens[i].text));
9394   setup.editor_cascade = seci;
9395 }
9396
9397 void LoadSetup()
9398 {
9399   char *filename = getSetupFilename();
9400   SetupFileHash *setup_file_hash = NULL;
9401
9402   /* always start with reliable default values */
9403   setSetupInfoToDefaults(&setup);
9404
9405   setup_file_hash = loadSetupFileHash(filename);
9406
9407   if (setup_file_hash)
9408   {
9409     char *player_name_new;
9410
9411     checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
9412     decodeSetupFileHash(setup_file_hash);
9413
9414     freeSetupFileHash(setup_file_hash);
9415
9416     /* needed to work around problems with fixed length strings */
9417     player_name_new = get_corrected_login_name(setup.player_name);
9418     free(setup.player_name);
9419     setup.player_name = player_name_new;
9420
9421     /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9422     if (setup.scroll_delay == FALSE)
9423     {
9424       setup.scroll_delay_value = MIN_SCROLL_DELAY;
9425       setup.scroll_delay = TRUE;                        /* now always "on" */
9426     }
9427
9428     /* make sure that scroll delay value stays inside valid range */
9429     setup.scroll_delay_value =
9430       MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9431   }
9432   else
9433     Error(ERR_WARN, "using default setup values");
9434 }
9435
9436 void LoadSetup_EditorCascade()
9437 {
9438   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9439   SetupFileHash *setup_file_hash = NULL;
9440
9441   /* always start with reliable default values */
9442   setSetupInfoToDefaults_EditorCascade(&setup);
9443
9444   setup_file_hash = loadSetupFileHash(filename);
9445
9446   if (setup_file_hash)
9447   {
9448     checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
9449     decodeSetupFileHash_EditorCascade(setup_file_hash);
9450
9451     freeSetupFileHash(setup_file_hash);
9452   }
9453
9454   free(filename);
9455 }
9456
9457 void SaveSetup()
9458 {
9459   char *filename = getSetupFilename();
9460   FILE *file;
9461   int i, pnr;
9462
9463   InitUserDataDirectory();
9464
9465   if (!(file = fopen(filename, MODE_WRITE)))
9466   {
9467     Error(ERR_WARN, "cannot write setup file '%s'", filename);
9468     return;
9469   }
9470
9471   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
9472                                                getCookie("SETUP")));
9473   fprintf(file, "\n");
9474
9475   /* global setup */
9476   si = setup;
9477   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9478   {
9479     /* just to make things nicer :) */
9480     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9481         i == SETUP_TOKEN_GRAPHICS_SET)
9482       fprintf(file, "\n");
9483
9484     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9485   }
9486
9487   /* editor setup */
9488   sei = setup.editor;
9489   fprintf(file, "\n");
9490   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9491     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9492
9493   /* shortcut setup */
9494   ssi = setup.shortcut;
9495   fprintf(file, "\n");
9496   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9497     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9498
9499   /* player setup */
9500   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9501   {
9502     char prefix[30];
9503
9504     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9505     fprintf(file, "\n");
9506
9507     sii = setup.input[pnr];
9508     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9509       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9510   }
9511
9512   /* system setup */
9513   syi = setup.system;
9514   fprintf(file, "\n");
9515   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9516     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9517
9518   /* options setup */
9519   soi = setup.options;
9520   fprintf(file, "\n");
9521   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9522     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9523
9524   fclose(file);
9525
9526   SetFilePermissions(filename, PERMS_PRIVATE);
9527 }
9528
9529 void SaveSetup_EditorCascade()
9530 {
9531   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9532   FILE *file;
9533   int i;
9534
9535   InitUserDataDirectory();
9536
9537   if (!(file = fopen(filename, MODE_WRITE)))
9538   {
9539     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9540     free(filename);
9541     return;
9542   }
9543
9544   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
9545                                                getCookie("SETUP")));
9546   fprintf(file, "\n");
9547
9548   seci = setup.editor_cascade;
9549   fprintf(file, "\n");
9550   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9551     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9552
9553   fclose(file);
9554
9555   SetFilePermissions(filename, PERMS_PRIVATE);
9556
9557   free(filename);
9558 }
9559
9560 void LoadCustomElementDescriptions()
9561 {
9562   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9563   SetupFileHash *setup_file_hash;
9564   int i;
9565
9566   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9567   {
9568     if (element_info[i].custom_description != NULL)
9569     {
9570       free(element_info[i].custom_description);
9571       element_info[i].custom_description = NULL;
9572     }
9573   }
9574
9575   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9576     return;
9577
9578   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9579   {
9580     char *token = getStringCat2(element_info[i].token_name, ".name");
9581     char *value = getHashEntry(setup_file_hash, token);
9582
9583     if (value != NULL)
9584       element_info[i].custom_description = getStringCopy(value);
9585
9586     free(token);
9587   }
9588
9589   freeSetupFileHash(setup_file_hash);
9590 }
9591
9592 static int getElementFromToken(char *token)
9593 {
9594 #if 1
9595   char *value = getHashEntry(element_token_hash, token);
9596
9597   if (value != NULL)
9598     return atoi(value);
9599 #else
9600   int i;
9601
9602   /* !!! OPTIMIZE THIS BY USING HASH !!! */
9603   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9604     if (strEqual(token, element_info[i].token_name))
9605       return i;
9606 #endif
9607
9608   Error(ERR_WARN, "unknown element token '%s'", token);
9609
9610   return EL_UNDEFINED;
9611 }
9612
9613 static int get_token_parameter_value(char *token, char *value_raw)
9614 {
9615   char *suffix;
9616
9617   if (token == NULL || value_raw == NULL)
9618     return ARG_UNDEFINED_VALUE;
9619
9620   suffix = strrchr(token, '.');
9621   if (suffix == NULL)
9622     suffix = token;
9623
9624 #if 1
9625   if (strEqual(suffix, ".element"))
9626     return getElementFromToken(value_raw);
9627 #endif
9628
9629 #if 0
9630   if (strncmp(suffix, ".font", 5) == 0)
9631   {
9632     int i;
9633
9634     /* !!! OPTIMIZE THIS BY USING HASH !!! */
9635     for (i = 0; i < NUM_FONTS; i++)
9636       if (strEqual(value_raw, font_info[i].token_name))
9637         return i;
9638
9639     /* if font not found, use reliable default value */
9640     return FONT_INITIAL_1;
9641   }
9642 #endif
9643
9644   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9645   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9646 }
9647
9648 void InitMenuDesignSettings_Static()
9649 {
9650 #if 0
9651   static SetupFileHash *image_config_hash = NULL;
9652 #endif
9653   int i;
9654
9655 #if 0
9656   if (image_config_hash == NULL)
9657   {
9658     image_config_hash = newSetupFileHash();
9659
9660     for (i = 0; image_config[i].token != NULL; i++)
9661       setHashEntry(image_config_hash,
9662                    image_config[i].token,
9663                    image_config[i].value);
9664   }
9665 #endif
9666
9667 #if 1
9668   /* always start with reliable default values from static default config */
9669   for (i = 0; image_config_vars[i].token != NULL; i++)
9670   {
9671     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9672
9673     if (value != NULL)
9674       *image_config_vars[i].value =
9675         get_token_parameter_value(image_config_vars[i].token, value);
9676   }
9677
9678 #else
9679
9680   int j;
9681
9682   /* always start with reliable default values from static default config */
9683   for (i = 0; image_config_vars[i].token != NULL; i++)
9684     for (j = 0; image_config[j].token != NULL; j++)
9685       if (strEqual(image_config_vars[i].token, image_config[j].token))
9686         *image_config_vars[i].value =
9687           get_token_parameter_value(image_config_vars[i].token,
9688                                     image_config[j].value);
9689 #endif
9690 }
9691
9692 static void InitMenuDesignSettings_SpecialPreProcessing()
9693 {
9694   int i;
9695
9696   /* the following initializes hierarchical values from static configuration */
9697
9698   /* special case: initialize "ARG_DEFAULT" values in static default config */
9699   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9700   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
9701   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9702   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9703   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9704   titlemessage_default.fade_mode  = title_default.fade_mode;
9705   titlemessage_default.fade_delay = title_default.fade_delay;
9706   titlemessage_default.post_delay = title_default.post_delay;
9707   titlemessage_default.auto_delay = title_default.auto_delay;
9708
9709   /* special case: initialize "ARG_DEFAULT" values in static default config */
9710   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9711   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9712   {
9713     titlemessage_initial[i] = titlemessage_initial_default;
9714     titlemessage[i] = titlemessage_default;
9715   }
9716
9717   /* special case: initialize "ARG_DEFAULT" values in static default config */
9718   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9719   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9720   {
9721     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9722     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9723   }
9724 }
9725
9726 static void InitMenuDesignSettings_SpecialPostProcessing()
9727 {
9728   /* special case: initialize later added SETUP list size from LEVELS value */
9729   if (menu.list_size[GAME_MODE_SETUP] == -1)
9730     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9731 }
9732
9733 static void LoadMenuDesignSettingsFromFilename(char *filename)
9734 {
9735   static struct TitleMessageInfo tmi;
9736   static struct TokenInfo titlemessage_tokens[] =
9737   {
9738     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9739     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9740     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9741     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9742     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9743     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9744     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9745     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9746     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9747     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9748     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9749     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9750     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9751     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9752     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9753     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9754     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9755
9756     { -1,               NULL,                   NULL                    }
9757   };
9758   static struct
9759   {
9760     struct TitleMessageInfo *array;
9761     char *text;
9762   }
9763   titlemessage_arrays[] =
9764   {
9765     { titlemessage_initial,             "[titlemessage_initial]"        },
9766     { titlemessage,                     "[titlemessage]"                },
9767
9768     { NULL,                             NULL                            }
9769   };
9770   SetupFileHash *setup_file_hash;
9771   int i, j, k;
9772
9773 #if 0
9774   printf("LoadMenuDesignSettings from file '%s' ...\n", filename);
9775 #endif
9776
9777   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9778     return;
9779
9780   /* the following initializes hierarchical values from dynamic configuration */
9781
9782   /* special case: initialize with default values that may be overwritten */
9783   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9784   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9785   {
9786     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9787     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9788     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9789
9790     if (value_1 != NULL)
9791       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9792     if (value_2 != NULL)
9793       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9794     if (value_3 != NULL)
9795       menu.list_size[i] = get_integer_from_string(value_3);
9796   }
9797
9798   /* special case: initialize with default values that may be overwritten */
9799   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9800   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9801   {
9802     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9803     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9804
9805     if (value_1 != NULL)
9806       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9807     if (value_2 != NULL)
9808       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9809   }
9810
9811   /* special case: initialize with default values that may be overwritten */
9812   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9813   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9814   {
9815     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9816     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9817
9818     if (value_1 != NULL)
9819       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9820     if (value_2 != NULL)
9821       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9822   }
9823
9824   /* special case: initialize with default values that may be overwritten */
9825   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9826   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9827   {
9828     char *token_1 = "menu.enter_screen.fade_mode";
9829     char *token_2 = "menu.enter_screen.fade_delay";
9830     char *token_3 = "menu.enter_screen.post_delay";
9831     char *token_4 = "menu.leave_screen.fade_mode";
9832     char *token_5 = "menu.leave_screen.fade_delay";
9833     char *token_6 = "menu.leave_screen.post_delay";
9834     char *value_1 = getHashEntry(setup_file_hash, token_1);
9835     char *value_2 = getHashEntry(setup_file_hash, token_2);
9836     char *value_3 = getHashEntry(setup_file_hash, token_3);
9837     char *value_4 = getHashEntry(setup_file_hash, token_4);
9838     char *value_5 = getHashEntry(setup_file_hash, token_5);
9839     char *value_6 = getHashEntry(setup_file_hash, token_6);
9840
9841     if (value_1 != NULL)
9842       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9843                                                                  value_1);
9844     if (value_2 != NULL)
9845       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9846                                                                   value_2);
9847     if (value_3 != NULL)
9848       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9849                                                                   value_3);
9850     if (value_4 != NULL)
9851       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9852                                                                  value_4);
9853     if (value_5 != NULL)
9854       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9855                                                                   value_5);
9856     if (value_6 != NULL)
9857       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9858                                                                   value_6);
9859   }
9860
9861   /* special case: initialize with default values that may be overwritten */
9862   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9863   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9864   {
9865     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9866     char *base_token = titlemessage_arrays[i].text;
9867
9868     for (j = 0; titlemessage_tokens[j].type != -1; j++)
9869     {
9870       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9871       char *value = getHashEntry(setup_file_hash, token);
9872
9873       if (value != NULL)
9874       {
9875         int parameter_value = get_token_parameter_value(token, value);
9876
9877         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9878         {
9879           tmi = array[k];
9880
9881           if (titlemessage_tokens[j].type == TYPE_INTEGER)
9882             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9883           else
9884             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
9885
9886           array[k] = tmi;
9887         }
9888       }
9889
9890       free(token);
9891     }
9892   }
9893
9894   /* read (and overwrite with) values that may be specified in config file */
9895   for (i = 0; image_config_vars[i].token != NULL; i++)
9896   {
9897     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9898
9899     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9900     if (value != NULL && !strEqual(value, ARG_DEFAULT))
9901       *image_config_vars[i].value =
9902         get_token_parameter_value(image_config_vars[i].token, value);
9903   }
9904
9905   freeSetupFileHash(setup_file_hash);
9906 }
9907
9908 void LoadMenuDesignSettings()
9909 {
9910   char *filename_base = UNDEFINED_FILENAME, *filename_local;
9911
9912   InitMenuDesignSettings_Static();
9913   InitMenuDesignSettings_SpecialPreProcessing();
9914
9915 #if 1
9916   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9917 #else
9918   if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
9919 #endif
9920   {
9921     /* first look for special settings configured in level series config */
9922     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9923
9924     if (fileExists(filename_base))
9925       LoadMenuDesignSettingsFromFilename(filename_base);
9926   }
9927
9928   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9929
9930   if (filename_local != NULL && !strEqual(filename_base, filename_local))
9931     LoadMenuDesignSettingsFromFilename(filename_local);
9932
9933   InitMenuDesignSettings_SpecialPostProcessing();
9934 }
9935
9936 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9937 {
9938   char *filename = getEditorSetupFilename();
9939   SetupFileList *setup_file_list, *list;
9940   SetupFileHash *element_hash;
9941   int num_unknown_tokens = 0;
9942   int i;
9943
9944   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9945     return;
9946
9947   element_hash = newSetupFileHash();
9948
9949   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9950     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9951
9952   /* determined size may be larger than needed (due to unknown elements) */
9953   *num_elements = 0;
9954   for (list = setup_file_list; list != NULL; list = list->next)
9955     (*num_elements)++;
9956
9957   /* add space for up to 3 more elements for padding that may be needed */
9958   *num_elements += 3;
9959
9960   /* free memory for old list of elements, if needed */
9961   checked_free(*elements);
9962
9963   /* allocate memory for new list of elements */
9964   *elements = checked_malloc(*num_elements * sizeof(int));
9965
9966   *num_elements = 0;
9967   for (list = setup_file_list; list != NULL; list = list->next)
9968   {
9969     char *value = getHashEntry(element_hash, list->token);
9970
9971     if (value == NULL)          /* try to find obsolete token mapping */
9972     {
9973       char *mapped_token = get_mapped_token(list->token);
9974
9975       if (mapped_token != NULL)
9976       {
9977         value = getHashEntry(element_hash, mapped_token);
9978
9979         free(mapped_token);
9980       }
9981     }
9982
9983     if (value != NULL)
9984     {
9985       (*elements)[(*num_elements)++] = atoi(value);
9986     }
9987     else
9988     {
9989       if (num_unknown_tokens == 0)
9990       {
9991         Error(ERR_INFO_LINE, "-");
9992         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9993         Error(ERR_INFO, "- config file: '%s'", filename);
9994
9995         num_unknown_tokens++;
9996       }
9997
9998       Error(ERR_INFO, "- token: '%s'", list->token);
9999     }
10000   }
10001
10002   if (num_unknown_tokens > 0)
10003     Error(ERR_INFO_LINE, "-");
10004
10005   while (*num_elements % 4)     /* pad with empty elements, if needed */
10006     (*elements)[(*num_elements)++] = EL_EMPTY;
10007
10008   freeSetupFileList(setup_file_list);
10009   freeSetupFileHash(element_hash);
10010
10011 #if 0
10012   for (i = 0; i < *num_elements; i++)
10013     printf("editor: element '%s' [%d]\n",
10014            element_info[(*elements)[i]].token_name, (*elements)[i]);
10015 #endif
10016 }
10017
10018 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10019                                                      boolean is_sound)
10020 {
10021   SetupFileHash *setup_file_hash = NULL;
10022   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10023   char *filename_music, *filename_prefix, *filename_info;
10024   struct
10025   {
10026     char *token;
10027     char **value_ptr;
10028   }
10029   token_to_value_ptr[] =
10030   {
10031     { "title_header",   &tmp_music_file_info.title_header       },
10032     { "artist_header",  &tmp_music_file_info.artist_header      },
10033     { "album_header",   &tmp_music_file_info.album_header       },
10034     { "year_header",    &tmp_music_file_info.year_header        },
10035
10036     { "title",          &tmp_music_file_info.title              },
10037     { "artist",         &tmp_music_file_info.artist             },
10038     { "album",          &tmp_music_file_info.album              },
10039     { "year",           &tmp_music_file_info.year               },
10040
10041     { NULL,             NULL                                    },
10042   };
10043   int i;
10044
10045   filename_music = (is_sound ? getCustomSoundFilename(basename) :
10046                     getCustomMusicFilename(basename));
10047
10048   if (filename_music == NULL)
10049     return NULL;
10050
10051   /* ---------- try to replace file extension ---------- */
10052
10053   filename_prefix = getStringCopy(filename_music);
10054   if (strrchr(filename_prefix, '.') != NULL)
10055     *strrchr(filename_prefix, '.') = '\0';
10056   filename_info = getStringCat2(filename_prefix, ".txt");
10057
10058 #if 0
10059   printf("trying to load file '%s'...\n", filename_info);
10060 #endif
10061
10062   if (fileExists(filename_info))
10063     setup_file_hash = loadSetupFileHash(filename_info);
10064
10065   free(filename_prefix);
10066   free(filename_info);
10067
10068   if (setup_file_hash == NULL)
10069   {
10070     /* ---------- try to add file extension ---------- */
10071
10072     filename_prefix = getStringCopy(filename_music);
10073     filename_info = getStringCat2(filename_prefix, ".txt");
10074
10075 #if 0
10076     printf("trying to load file '%s'...\n", filename_info);
10077 #endif
10078
10079     if (fileExists(filename_info))
10080       setup_file_hash = loadSetupFileHash(filename_info);
10081
10082     free(filename_prefix);
10083     free(filename_info);
10084   }
10085
10086   if (setup_file_hash == NULL)
10087     return NULL;
10088
10089   /* ---------- music file info found ---------- */
10090
10091   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10092
10093   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10094   {
10095     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10096
10097     *token_to_value_ptr[i].value_ptr =
10098       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10099   }
10100
10101   tmp_music_file_info.basename = getStringCopy(basename);
10102   tmp_music_file_info.music = music;
10103   tmp_music_file_info.is_sound = is_sound;
10104
10105   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10106   *new_music_file_info = tmp_music_file_info;
10107
10108   return new_music_file_info;
10109 }
10110
10111 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10112 {
10113   return get_music_file_info_ext(basename, music, FALSE);
10114 }
10115
10116 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10117 {
10118   return get_music_file_info_ext(basename, sound, TRUE);
10119 }
10120
10121 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10122                                      char *basename, boolean is_sound)
10123 {
10124   for (; list != NULL; list = list->next)
10125     if (list->is_sound == is_sound && strEqual(list->basename, basename))
10126       return TRUE;
10127
10128   return FALSE;
10129 }
10130
10131 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10132 {
10133   return music_info_listed_ext(list, basename, FALSE);
10134 }
10135
10136 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10137 {
10138   return music_info_listed_ext(list, basename, TRUE);
10139 }
10140
10141 void LoadMusicInfo()
10142 {
10143   char *music_directory = getCustomMusicDirectory();
10144   int num_music = getMusicListSize();
10145   int num_music_noconf = 0;
10146   int num_sounds = getSoundListSize();
10147   DIR *dir;
10148   struct dirent *dir_entry;
10149   struct FileInfo *music, *sound;
10150   struct MusicFileInfo *next, **new;
10151   int i;
10152
10153   while (music_file_info != NULL)
10154   {
10155     next = music_file_info->next;
10156
10157     checked_free(music_file_info->basename);
10158
10159     checked_free(music_file_info->title_header);
10160     checked_free(music_file_info->artist_header);
10161     checked_free(music_file_info->album_header);
10162     checked_free(music_file_info->year_header);
10163
10164     checked_free(music_file_info->title);
10165     checked_free(music_file_info->artist);
10166     checked_free(music_file_info->album);
10167     checked_free(music_file_info->year);
10168
10169     free(music_file_info);
10170
10171     music_file_info = next;
10172   }
10173
10174   new = &music_file_info;
10175
10176   for (i = 0; i < num_music; i++)
10177   {
10178     music = getMusicListEntry(i);
10179
10180     if (music->filename == NULL)
10181       continue;
10182
10183     if (strEqual(music->filename, UNDEFINED_FILENAME))
10184       continue;
10185
10186     /* a configured file may be not recognized as music */
10187     if (!FileIsMusic(music->filename))
10188       continue;
10189
10190 #if 0
10191     printf("::: -> '%s' (configured)\n", music->filename);
10192 #endif
10193
10194     if (!music_info_listed(music_file_info, music->filename))
10195     {
10196       *new = get_music_file_info(music->filename, i);
10197       if (*new != NULL)
10198         new = &(*new)->next;
10199     }
10200   }
10201
10202   if ((dir = opendir(music_directory)) == NULL)
10203   {
10204     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10205     return;
10206   }
10207
10208   while ((dir_entry = readdir(dir)) != NULL)    /* loop until last dir entry */
10209   {
10210     char *basename = dir_entry->d_name;
10211     boolean music_already_used = FALSE;
10212     int i;
10213
10214     /* skip all music files that are configured in music config file */
10215     for (i = 0; i < num_music; i++)
10216     {
10217       music = getMusicListEntry(i);
10218
10219       if (music->filename == NULL)
10220         continue;
10221
10222       if (strEqual(basename, music->filename))
10223       {
10224         music_already_used = TRUE;
10225         break;
10226       }
10227     }
10228
10229     if (music_already_used)
10230       continue;
10231
10232     if (!FileIsMusic(basename))
10233       continue;
10234
10235 #if 0
10236     printf("::: -> '%s' (found in directory)\n", basename);
10237 #endif
10238
10239     if (!music_info_listed(music_file_info, basename))
10240     {
10241       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10242       if (*new != NULL)
10243         new = &(*new)->next;
10244     }
10245
10246     num_music_noconf++;
10247   }
10248
10249   closedir(dir);
10250
10251   for (i = 0; i < num_sounds; i++)
10252   {
10253     sound = getSoundListEntry(i);
10254
10255     if (sound->filename == NULL)
10256       continue;
10257
10258     if (strEqual(sound->filename, UNDEFINED_FILENAME))
10259       continue;
10260
10261     /* a configured file may be not recognized as sound */
10262     if (!FileIsSound(sound->filename))
10263       continue;
10264
10265 #if 0
10266     printf("::: -> '%s' (configured)\n", sound->filename);
10267 #endif
10268
10269     if (!sound_info_listed(music_file_info, sound->filename))
10270     {
10271       *new = get_sound_file_info(sound->filename, i);
10272       if (*new != NULL)
10273         new = &(*new)->next;
10274     }
10275   }
10276
10277 #if 0
10278   for (next = music_file_info; next != NULL; next = next->next)
10279     printf("::: title == '%s'\n", next->title);
10280 #endif
10281 }
10282
10283 void add_helpanim_entry(int element, int action, int direction, int delay,
10284                         int *num_list_entries)
10285 {
10286   struct HelpAnimInfo *new_list_entry;
10287   (*num_list_entries)++;
10288
10289   helpanim_info =
10290     checked_realloc(helpanim_info,
10291                     *num_list_entries * sizeof(struct HelpAnimInfo));
10292   new_list_entry = &helpanim_info[*num_list_entries - 1];
10293
10294   new_list_entry->element = element;
10295   new_list_entry->action = action;
10296   new_list_entry->direction = direction;
10297   new_list_entry->delay = delay;
10298 }
10299
10300 void print_unknown_token(char *filename, char *token, int token_nr)
10301 {
10302   if (token_nr == 0)
10303   {
10304     Error(ERR_INFO_LINE, "-");
10305     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10306     Error(ERR_INFO, "- config file: '%s'", filename);
10307   }
10308
10309   Error(ERR_INFO, "- token: '%s'", token);
10310 }
10311
10312 void print_unknown_token_end(int token_nr)
10313 {
10314   if (token_nr > 0)
10315     Error(ERR_INFO_LINE, "-");
10316 }
10317
10318 void LoadHelpAnimInfo()
10319 {
10320   char *filename = getHelpAnimFilename();
10321   SetupFileList *setup_file_list = NULL, *list;
10322   SetupFileHash *element_hash, *action_hash, *direction_hash;
10323   int num_list_entries = 0;
10324   int num_unknown_tokens = 0;
10325   int i;
10326
10327   if (fileExists(filename))
10328     setup_file_list = loadSetupFileList(filename);
10329
10330   if (setup_file_list == NULL)
10331   {
10332     /* use reliable default values from static configuration */
10333     SetupFileList *insert_ptr;
10334
10335     insert_ptr = setup_file_list =
10336       newSetupFileList(helpanim_config[0].token,
10337                        helpanim_config[0].value);
10338
10339     for (i = 1; helpanim_config[i].token; i++)
10340       insert_ptr = addListEntry(insert_ptr,
10341                                 helpanim_config[i].token,
10342                                 helpanim_config[i].value);
10343   }
10344
10345   element_hash   = newSetupFileHash();
10346   action_hash    = newSetupFileHash();
10347   direction_hash = newSetupFileHash();
10348
10349   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10350     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10351
10352   for (i = 0; i < NUM_ACTIONS; i++)
10353     setHashEntry(action_hash, element_action_info[i].suffix,
10354                  i_to_a(element_action_info[i].value));
10355
10356   /* do not store direction index (bit) here, but direction value! */
10357   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10358     setHashEntry(direction_hash, element_direction_info[i].suffix,
10359                  i_to_a(1 << element_direction_info[i].value));
10360
10361   for (list = setup_file_list; list != NULL; list = list->next)
10362   {
10363     char *element_token, *action_token, *direction_token;
10364     char *element_value, *action_value, *direction_value;
10365     int delay = atoi(list->value);
10366
10367     if (strEqual(list->token, "end"))
10368     {
10369       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10370
10371       continue;
10372     }
10373
10374     /* first try to break element into element/action/direction parts;
10375        if this does not work, also accept combined "element[.act][.dir]"
10376        elements (like "dynamite.active"), which are unique elements */
10377
10378     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
10379     {
10380       element_value = getHashEntry(element_hash, list->token);
10381       if (element_value != NULL)        /* element found */
10382         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10383                            &num_list_entries);
10384       else
10385       {
10386         /* no further suffixes found -- this is not an element */
10387         print_unknown_token(filename, list->token, num_unknown_tokens++);
10388       }
10389
10390       continue;
10391     }
10392
10393     /* token has format "<prefix>.<something>" */
10394
10395     action_token = strchr(list->token, '.');    /* suffix may be action ... */
10396     direction_token = action_token;             /* ... or direction */
10397
10398     element_token = getStringCopy(list->token);
10399     *strchr(element_token, '.') = '\0';
10400
10401     element_value = getHashEntry(element_hash, element_token);
10402
10403     if (element_value == NULL)          /* this is no element */
10404     {
10405       element_value = getHashEntry(element_hash, list->token);
10406       if (element_value != NULL)        /* combined element found */
10407         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10408                            &num_list_entries);
10409       else
10410         print_unknown_token(filename, list->token, num_unknown_tokens++);
10411
10412       free(element_token);
10413
10414       continue;
10415     }
10416
10417     action_value = getHashEntry(action_hash, action_token);
10418
10419     if (action_value != NULL)           /* action found */
10420     {
10421       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10422                     &num_list_entries);
10423
10424       free(element_token);
10425
10426       continue;
10427     }
10428
10429     direction_value = getHashEntry(direction_hash, direction_token);
10430
10431     if (direction_value != NULL)        /* direction found */
10432     {
10433       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10434                          &num_list_entries);
10435
10436       free(element_token);
10437
10438       continue;
10439     }
10440
10441     if (strchr(action_token + 1, '.') == NULL)
10442     {
10443       /* no further suffixes found -- this is not an action nor direction */
10444
10445       element_value = getHashEntry(element_hash, list->token);
10446       if (element_value != NULL)        /* combined element found */
10447         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10448                            &num_list_entries);
10449       else
10450         print_unknown_token(filename, list->token, num_unknown_tokens++);
10451
10452       free(element_token);
10453
10454       continue;
10455     }
10456
10457     /* token has format "<prefix>.<suffix>.<something>" */
10458
10459     direction_token = strchr(action_token + 1, '.');
10460
10461     action_token = getStringCopy(action_token);
10462     *strchr(action_token + 1, '.') = '\0';
10463
10464     action_value = getHashEntry(action_hash, action_token);
10465
10466     if (action_value == NULL)           /* this is no action */
10467     {
10468       element_value = getHashEntry(element_hash, list->token);
10469       if (element_value != NULL)        /* combined element found */
10470         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10471                            &num_list_entries);
10472       else
10473         print_unknown_token(filename, list->token, num_unknown_tokens++);
10474
10475       free(element_token);
10476       free(action_token);
10477
10478       continue;
10479     }
10480
10481     direction_value = getHashEntry(direction_hash, direction_token);
10482
10483     if (direction_value != NULL)        /* direction found */
10484     {
10485       add_helpanim_entry(atoi(element_value), atoi(action_value),
10486                          atoi(direction_value), delay, &num_list_entries);
10487
10488       free(element_token);
10489       free(action_token);
10490
10491       continue;
10492     }
10493
10494     /* this is no direction */
10495
10496     element_value = getHashEntry(element_hash, list->token);
10497     if (element_value != NULL)          /* combined element found */
10498       add_helpanim_entry(atoi(element_value), -1, -1, delay,
10499                          &num_list_entries);
10500     else
10501       print_unknown_token(filename, list->token, num_unknown_tokens++);
10502
10503     free(element_token);
10504     free(action_token);
10505   }
10506
10507   print_unknown_token_end(num_unknown_tokens);
10508
10509   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10510   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
10511
10512   freeSetupFileList(setup_file_list);
10513   freeSetupFileHash(element_hash);
10514   freeSetupFileHash(action_hash);
10515   freeSetupFileHash(direction_hash);
10516
10517 #if 0
10518   for (i = 0; i < num_list_entries; i++)
10519     printf("::: '%s': %d, %d, %d => %d\n",
10520            EL_NAME(helpanim_info[i].element),
10521            helpanim_info[i].element,
10522            helpanim_info[i].action,
10523            helpanim_info[i].direction,
10524            helpanim_info[i].delay);
10525 #endif
10526 }
10527
10528 void LoadHelpTextInfo()
10529 {
10530   char *filename = getHelpTextFilename();
10531   int i;
10532
10533   if (helptext_info != NULL)
10534   {
10535     freeSetupFileHash(helptext_info);
10536     helptext_info = NULL;
10537   }
10538
10539   if (fileExists(filename))
10540     helptext_info = loadSetupFileHash(filename);
10541
10542   if (helptext_info == NULL)
10543   {
10544     /* use reliable default values from static configuration */
10545     helptext_info = newSetupFileHash();
10546
10547     for (i = 0; helptext_config[i].token; i++)
10548       setHashEntry(helptext_info,
10549                    helptext_config[i].token,
10550                    helptext_config[i].value);
10551   }
10552
10553 #if 0
10554   BEGIN_HASH_ITERATION(helptext_info, itr)
10555   {
10556     printf("::: '%s' => '%s'\n",
10557            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10558   }
10559   END_HASH_ITERATION(hash, itr)
10560 #endif
10561 }
10562
10563
10564 /* ------------------------------------------------------------------------- */
10565 /* convert levels                                                            */
10566 /* ------------------------------------------------------------------------- */
10567
10568 #define MAX_NUM_CONVERT_LEVELS          1000
10569
10570 void ConvertLevels()
10571 {
10572   static LevelDirTree *convert_leveldir = NULL;
10573   static int convert_level_nr = -1;
10574   static int num_levels_handled = 0;
10575   static int num_levels_converted = 0;
10576   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10577   int i;
10578
10579   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10580                                                global.convert_leveldir);
10581
10582   if (convert_leveldir == NULL)
10583     Error(ERR_EXIT, "no such level identifier: '%s'",
10584           global.convert_leveldir);
10585
10586   leveldir_current = convert_leveldir;
10587
10588   if (global.convert_level_nr != -1)
10589   {
10590     convert_leveldir->first_level = global.convert_level_nr;
10591     convert_leveldir->last_level  = global.convert_level_nr;
10592   }
10593
10594   convert_level_nr = convert_leveldir->first_level;
10595
10596   printf_line("=", 79);
10597   printf("Converting levels\n");
10598   printf_line("-", 79);
10599   printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
10600   printf("Level series name:       '%s'\n", convert_leveldir->name);
10601   printf("Level series author:     '%s'\n", convert_leveldir->author);
10602   printf("Number of levels:        %d\n",   convert_leveldir->levels);
10603   printf_line("=", 79);
10604   printf("\n");
10605
10606   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10607     levels_failed[i] = FALSE;
10608
10609   while (convert_level_nr <= convert_leveldir->last_level)
10610   {
10611     char *level_filename;
10612     boolean new_level;
10613
10614     level_nr = convert_level_nr++;
10615
10616     printf("Level %03d: ", level_nr);
10617
10618     LoadLevel(level_nr);
10619     if (level.no_valid_file)
10620     {
10621       printf("(no level)\n");
10622       continue;
10623     }
10624
10625     printf("converting level ... ");
10626
10627     level_filename = getDefaultLevelFilename(level_nr);
10628     new_level = !fileExists(level_filename);
10629
10630     if (new_level)
10631     {
10632       SaveLevel(level_nr);
10633
10634       num_levels_converted++;
10635
10636       printf("converted.\n");
10637     }
10638     else
10639     {
10640       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10641         levels_failed[level_nr] = TRUE;
10642
10643       printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10644     }
10645
10646     num_levels_handled++;
10647   }
10648
10649   printf("\n");
10650   printf_line("=", 79);
10651   printf("Number of levels handled: %d\n", num_levels_handled);
10652   printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10653          (num_levels_handled ?
10654           num_levels_converted * 100 / num_levels_handled : 0));
10655   printf_line("-", 79);
10656   printf("Summary (for automatic parsing by scripts):\n");
10657   printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10658          convert_leveldir->identifier, num_levels_converted,
10659          num_levels_handled,
10660          (num_levels_handled ?
10661           num_levels_converted * 100 / num_levels_handled : 0));
10662
10663   if (num_levels_handled != num_levels_converted)
10664   {
10665     printf(", FAILED:");
10666     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10667       if (levels_failed[i])
10668         printf(" %03d", i);
10669   }
10670
10671   printf("\n");
10672   printf_line("=", 79);
10673
10674   CloseAllAndExit(0);
10675 }
10676
10677
10678 /* ------------------------------------------------------------------------- */
10679 /* create and save images for use in level sketches (raw BMP format)         */
10680 /* ------------------------------------------------------------------------- */
10681
10682 void CreateLevelSketchImages()
10683 {
10684 #if defined(TARGET_SDL)
10685   Bitmap *bitmap1;
10686   Bitmap *bitmap2;
10687   int i;
10688
10689   InitElementPropertiesGfxElement();
10690
10691   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10692   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10693
10694   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10695   {
10696     Bitmap *src_bitmap;
10697     int src_x, src_y;
10698     int element = getMappedElement(i);
10699     int graphic = el2edimg(element);
10700     char basename1[16];
10701     char basename2[16];
10702     char *filename1;
10703     char *filename2;
10704
10705     sprintf(basename1, "%03d.bmp", i);
10706     sprintf(basename2, "%03ds.bmp", i);
10707
10708     filename1 = getPath2(global.create_images_dir, basename1);
10709     filename2 = getPath2(global.create_images_dir, basename2);
10710
10711     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10712     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY, 0, 0);
10713
10714     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10715       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10716
10717     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10718     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10719
10720     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10721       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10722
10723     free(filename1);
10724     free(filename2);
10725
10726     if (options.debug)
10727       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10728   }
10729
10730   FreeBitmap(bitmap1);
10731   FreeBitmap(bitmap2);
10732
10733   if (options.debug)
10734     printf("\n");
10735
10736   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10737
10738   CloseAllAndExit(0);
10739 #endif
10740 }
10741
10742
10743 /* ------------------------------------------------------------------------- */
10744 /* create and save images for custom and group elements (raw BMP format)     */
10745 /* ------------------------------------------------------------------------- */
10746
10747 void CreateCustomElementImages()
10748 {
10749 #if defined(TARGET_SDL)
10750   char *filename = "graphics.classic/RocksCE.bmp";
10751   Bitmap *bitmap;
10752   Bitmap *src_bitmap;
10753   int dummy_graphic = IMG_CUSTOM_99;
10754   int yoffset_ce = 0;
10755   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10756   int src_x, src_y;
10757   int i;
10758
10759   bitmap = CreateBitmap(TILEX * 16 * 2,
10760                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10761                         DEFAULT_DEPTH);
10762
10763   getGraphicSource(dummy_graphic, 0, &src_bitmap, &src_x, &src_y);
10764
10765   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10766   {
10767     int x = i % 16;
10768     int y = i / 16;
10769     int ii = i + 1;
10770     int j;
10771
10772     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10773                TILEX * x, TILEY * y + yoffset_ce);
10774
10775     BlitBitmap(src_bitmap, bitmap, 0, TILEY, TILEX, TILEY,
10776                TILEX * x + TILEX * 16, TILEY * y + yoffset_ce);
10777
10778     for (j = 2; j >= 0; j--)
10779     {
10780       int c = ii % 10;
10781
10782       BlitBitmap(src_bitmap, bitmap, TILEX + c * 7, 0, 6, 10,
10783                  TILEX * x + 6 + j * 7,
10784                  TILEY * y + 11 + yoffset_ce);
10785
10786       BlitBitmap(src_bitmap, bitmap, TILEX + c * 8, TILEY, 6, 10,
10787                  TILEX * 16 + TILEX * x + 6 + j * 8,
10788                  TILEY * y + 10 + yoffset_ce);
10789
10790       ii /= 10;
10791     }
10792   }
10793
10794   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10795   {
10796     int x = i % 16;
10797     int y = i / 16;
10798     int ii = i + 1;
10799     int j;
10800
10801     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10802                TILEX * x, TILEY * y + yoffset_ge);
10803
10804     BlitBitmap(src_bitmap, bitmap, 0, TILEY, TILEX, TILEY,
10805                TILEX * x + TILEX * 16, TILEY * y + yoffset_ge);
10806
10807     for (j = 1; j >= 0; j--)
10808     {
10809       int c = ii % 10;
10810
10811       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10812                  TILEX * x + 6 + j * 10,
10813                  TILEY * y + 11 + yoffset_ge);
10814
10815       BlitBitmap(src_bitmap, bitmap, TILEX + c * 8, TILEY + 12, 6, 10,
10816                  TILEX * 16 + TILEX * x + 10 + j * 8,
10817                  TILEY * y + 10 + yoffset_ge);
10818
10819       ii /= 10;
10820     }
10821   }
10822
10823   if (SDL_SaveBMP(bitmap->surface, filename) != 0)
10824     Error(ERR_EXIT, "cannot save CE graphics file '%s'", filename);
10825
10826   FreeBitmap(bitmap);
10827
10828   CloseAllAndExit(0);
10829 #endif
10830 }
10831
10832 #if 0
10833 void CreateLevelSketchImages_TEST()
10834 {
10835   void CreateCustomElementImages()
10836 }
10837 #endif