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