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