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