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