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