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