re-added loading/saving editor settings for element cascade headlines
[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_TIME_LIMIT                  17
7949 #define SETUP_TOKEN_FULLSCREEN                  18
7950 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT      19
7951 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY      20
7952 #define SETUP_TOKEN_SCREEN_RENDERING_MODE       21
7953 #define SETUP_TOKEN_ASK_ON_ESCAPE               22
7954 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        23
7955 #define SETUP_TOKEN_QUICK_SWITCH                24
7956 #define SETUP_TOKEN_INPUT_ON_FOCUS              25
7957 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         26
7958 #define SETUP_TOKEN_GAME_FRAME_DELAY            27
7959 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS     28
7960 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS         29
7961 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS       30
7962 #define SETUP_TOKEN_GRAPHICS_SET                31
7963 #define SETUP_TOKEN_SOUNDS_SET                  32
7964 #define SETUP_TOKEN_MUSIC_SET                   33
7965 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     34
7966 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       35
7967 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        36
7968 #define SETUP_TOKEN_VOLUME_SIMPLE               37
7969 #define SETUP_TOKEN_VOLUME_LOOPS                38
7970 #define SETUP_TOKEN_VOLUME_MUSIC                39
7971 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE          40
7972 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE         41
7973 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE         42
7974
7975 #define NUM_GLOBAL_SETUP_TOKENS                 43
7976
7977 /* editor setup */
7978 #define SETUP_TOKEN_EDITOR_EL_CLASSIC           0
7979 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            1
7980 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      2
7981 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           3
7982 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         4
7983 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   5
7984
7985 #define NUM_EDITOR_SETUP_TOKENS                 6
7986
7987 /* editor cascade setup */
7988 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
7989 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
7990 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
7991 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
7992 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
7993 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
7994 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
7995 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
7996 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
7997 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT    9
7998 #define SETUP_TOKEN_EDITOR_CASCADE_CE           10
7999 #define SETUP_TOKEN_EDITOR_CASCADE_GE           11
8000 #define SETUP_TOKEN_EDITOR_CASCADE_REF          12
8001 #define SETUP_TOKEN_EDITOR_CASCADE_USER         13
8002 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      14
8003
8004 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         15
8005
8006 /* shortcut setup */
8007 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
8008 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
8009 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
8010 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
8011 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
8012 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
8013 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
8014 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
8015 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT         8
8016 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA         9
8017 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP          10
8018 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE         11
8019 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD        12
8020 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY          13
8021 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE       14
8022 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS        15
8023 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC        16
8024 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT          17
8025 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT         18
8026 #define SETUP_TOKEN_SHORTCUT_SNAP_UP            19
8027 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN          20
8028
8029 #define NUM_SHORTCUT_SETUP_TOKENS               21
8030
8031 /* player setup */
8032 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
8033 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
8034 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
8035 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
8036 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
8037 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
8038 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
8039 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
8040 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
8041 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
8042 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
8043 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
8044 #define SETUP_TOKEN_PLAYER_KEY_UP               12
8045 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
8046 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
8047 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
8048
8049 #define NUM_PLAYER_SETUP_TOKENS                 16
8050
8051 /* system setup */
8052 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER      0
8053 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      1
8054 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  2
8055
8056 #define NUM_SYSTEM_SETUP_TOKENS                 3
8057
8058 /* internal setup */
8059 #define SETUP_TOKEN_INT_PROGRAM_TITLE           0
8060 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR          1
8061 #define SETUP_TOKEN_INT_PROGRAM_EMAIL           2
8062 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE         3
8063 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT       4
8064 #define SETUP_TOKEN_INT_PROGRAM_COMPANY         5
8065 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE       6
8066 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET    7
8067 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET      8
8068 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET       9
8069 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE  10
8070 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE    11
8071 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE     12
8072 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES    13
8073 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14
8074 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH    15
8075 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT   16
8076
8077 #define NUM_INTERNAL_SETUP_TOKENS               17
8078
8079 /* debug setup */
8080 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0         0
8081 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1         1
8082 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2         2
8083 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3         3
8084 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4         4
8085 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5         5
8086 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6         6
8087 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7         7
8088 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8         8
8089 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9         9
8090 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0     10
8091 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1     11
8092 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2     12
8093 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3     13
8094 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4     14
8095 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5     15
8096 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6     16
8097 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7     17
8098 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8     18
8099 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9     19
8100 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8101 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8102
8103 #define NUM_DEBUG_SETUP_TOKENS                  22
8104
8105 /* options setup */
8106 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
8107
8108 #define NUM_OPTIONS_SETUP_TOKENS                1
8109
8110
8111 static struct SetupInfo si;
8112 static struct SetupEditorInfo sei;
8113 static struct SetupEditorCascadeInfo seci;
8114 static struct SetupShortcutInfo ssi;
8115 static struct SetupInputInfo sii;
8116 static struct SetupSystemInfo syi;
8117 static struct SetupInternalInfo sxi;
8118 static struct SetupDebugInfo sdi;
8119 static struct OptionInfo soi;
8120
8121 static struct TokenInfo global_setup_tokens[] =
8122 {
8123   { TYPE_STRING, &si.player_name,             "player_name"             },
8124   { TYPE_SWITCH, &si.sound,                   "sound"                   },
8125   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
8126   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
8127   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
8128   { TYPE_SWITCH, &si.toons,                   "toons"                   },
8129   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
8130   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
8131   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"    },
8132   { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory"  },
8133   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
8134   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
8135   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
8136   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
8137   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
8138   { TYPE_SWITCH, &si.handicap,                "handicap"                },
8139   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
8140   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
8141   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
8142   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
8143   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
8144   { TYPE_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"   },
8145   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
8146   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
8147   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
8148   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
8149   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
8150   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
8151   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8152   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
8153   { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"   },
8154   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
8155   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
8156   { TYPE_STRING, &si.music_set,               "music_set"               },
8157   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8158   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
8159   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
8160   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
8161   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
8162   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
8163   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
8164   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
8165   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
8166 };
8167
8168 static struct TokenInfo editor_setup_tokens[] =
8169 {
8170   { TYPE_SWITCH, &sei.el_classic,       "editor.el_classic"             },
8171   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
8172   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
8173   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
8174   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
8175   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
8176 };
8177
8178 static struct TokenInfo editor_cascade_setup_tokens[] =
8179 {
8180   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
8181   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
8182   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
8183   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
8184   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
8185   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
8186   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
8187   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
8188   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
8189   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
8190   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
8191   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
8192   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
8193   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
8194   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
8195 };
8196
8197 static struct TokenInfo shortcut_setup_tokens[] =
8198 {
8199   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
8200   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
8201   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
8202   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
8203   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
8204   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
8205   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
8206   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
8207   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
8208   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
8209   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
8210   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
8211   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
8212   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
8213   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
8214   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
8215   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
8216   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
8217   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
8218   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
8219   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
8220 };
8221
8222 static struct TokenInfo player_setup_tokens[] =
8223 {
8224   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
8225   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
8226   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
8227   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
8228   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
8229   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
8230   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
8231   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
8232   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
8233   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
8234   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
8235   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
8236   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
8237   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
8238   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
8239   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
8240 };
8241
8242 static struct TokenInfo system_setup_tokens[] =
8243 {
8244   { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"     },
8245   { TYPE_STRING,  &syi.sdl_audiodriver,    "system.sdl_audiodriver"     },
8246   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8247 };
8248
8249 static struct TokenInfo internal_setup_tokens[] =
8250 {
8251   { TYPE_STRING, &sxi.program_title,            "program_title"         },
8252   { TYPE_STRING, &sxi.program_author,           "program_author"        },
8253   { TYPE_STRING, &sxi.program_email,            "program_email"         },
8254   { TYPE_STRING, &sxi.program_website,          "program_website"       },
8255   { TYPE_STRING, &sxi.program_copyright,        "program_copyright"     },
8256   { TYPE_STRING, &sxi.program_company,          "program_company"       },
8257   { TYPE_STRING, &sxi.program_icon_file,        "program_icon_file"     },
8258   { TYPE_STRING, &sxi.default_graphics_set,     "default_graphics_set"  },
8259   { TYPE_STRING, &sxi.default_sounds_set,       "default_sounds_set"    },
8260   { TYPE_STRING, &sxi.default_music_set,        "default_music_set"     },
8261   { TYPE_STRING, &sxi.fallback_graphics_file,   "fallback_graphics_file"},
8262   { TYPE_STRING, &sxi.fallback_sounds_file,     "fallback_sounds_file"  },
8263   { TYPE_STRING, &sxi.fallback_music_file,      "fallback_music_file"   },
8264   { TYPE_STRING, &sxi.default_level_series,     "default_level_series"  },
8265   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8266   { TYPE_INTEGER,&sxi.default_window_width,     "default_window_width"  },
8267   { TYPE_INTEGER,&sxi.default_window_height,    "default_window_height" },
8268 };
8269
8270 static struct TokenInfo debug_setup_tokens[] =
8271 {
8272   { TYPE_INTEGER, &sdi.frame_delay[0],          "debug.frame_delay_0"   },
8273   { TYPE_INTEGER, &sdi.frame_delay[1],          "debug.frame_delay_1"   },
8274   { TYPE_INTEGER, &sdi.frame_delay[2],          "debug.frame_delay_2"   },
8275   { TYPE_INTEGER, &sdi.frame_delay[3],          "debug.frame_delay_3"   },
8276   { TYPE_INTEGER, &sdi.frame_delay[4],          "debug.frame_delay_4"   },
8277   { TYPE_INTEGER, &sdi.frame_delay[5],          "debug.frame_delay_5"   },
8278   { TYPE_INTEGER, &sdi.frame_delay[6],          "debug.frame_delay_6"   },
8279   { TYPE_INTEGER, &sdi.frame_delay[7],          "debug.frame_delay_7"   },
8280   { TYPE_INTEGER, &sdi.frame_delay[8],          "debug.frame_delay_8"   },
8281   { TYPE_INTEGER, &sdi.frame_delay[9],          "debug.frame_delay_9"   },
8282   { TYPE_KEY_X11, &sdi.frame_delay_key[0],      "debug.key.frame_delay_0" },
8283   { TYPE_KEY_X11, &sdi.frame_delay_key[1],      "debug.key.frame_delay_1" },
8284   { TYPE_KEY_X11, &sdi.frame_delay_key[2],      "debug.key.frame_delay_2" },
8285   { TYPE_KEY_X11, &sdi.frame_delay_key[3],      "debug.key.frame_delay_3" },
8286   { TYPE_KEY_X11, &sdi.frame_delay_key[4],      "debug.key.frame_delay_4" },
8287   { TYPE_KEY_X11, &sdi.frame_delay_key[5],      "debug.key.frame_delay_5" },
8288   { TYPE_KEY_X11, &sdi.frame_delay_key[6],      "debug.key.frame_delay_6" },
8289   { TYPE_KEY_X11, &sdi.frame_delay_key[7],      "debug.key.frame_delay_7" },
8290   { TYPE_KEY_X11, &sdi.frame_delay_key[8],      "debug.key.frame_delay_8" },
8291   { TYPE_KEY_X11, &sdi.frame_delay_key[9],      "debug.key.frame_delay_9" },
8292   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8293   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
8294 };
8295
8296 static struct TokenInfo options_setup_tokens[] =
8297 {
8298   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
8299 };
8300
8301 static char *get_corrected_login_name(char *login_name)
8302 {
8303   /* needed because player name must be a fixed length string */
8304   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8305
8306   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8307   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8308
8309   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
8310     if (strchr(login_name_new, ' '))
8311       *strchr(login_name_new, ' ') = '\0';
8312
8313   return login_name_new;
8314 }
8315
8316 static void setSetupInfoToDefaults(struct SetupInfo *si)
8317 {
8318   int i;
8319
8320   si->player_name = get_corrected_login_name(getLoginName());
8321
8322   si->sound = TRUE;
8323   si->sound_loops = TRUE;
8324   si->sound_music = TRUE;
8325   si->sound_simple = TRUE;
8326   si->toons = TRUE;
8327   si->scroll_delay = TRUE;
8328   si->scroll_delay_value = STD_SCROLL_DELAY;
8329   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8330   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8331   si->fade_screens = TRUE;
8332   si->autorecord = TRUE;
8333   si->show_titlescreen = TRUE;
8334   si->quick_doors = FALSE;
8335   si->team_mode = FALSE;
8336   si->handicap = TRUE;
8337   si->skip_levels = TRUE;
8338   si->time_limit = TRUE;
8339   si->fullscreen = FALSE;
8340   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8341   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8342   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8343   si->ask_on_escape = TRUE;
8344   si->ask_on_escape_editor = TRUE;
8345   si->quick_switch = FALSE;
8346   si->input_on_focus = FALSE;
8347   si->prefer_aga_graphics = TRUE;
8348   si->game_frame_delay = GAME_FRAME_DELAY;
8349   si->sp_show_border_elements = FALSE;
8350   si->small_game_graphics = FALSE;
8351   si->show_snapshot_buttons = FALSE;
8352
8353   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8354   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8355   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8356
8357   si->override_level_graphics = FALSE;
8358   si->override_level_sounds = FALSE;
8359   si->override_level_music = FALSE;
8360
8361   si->volume_simple = 100;              /* percent */
8362   si->volume_loops = 100;               /* percent */
8363   si->volume_music = 100;               /* percent */
8364
8365   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8366   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
8367   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
8368
8369   si->editor.el_boulderdash             = TRUE;
8370   si->editor.el_emerald_mine            = TRUE;
8371   si->editor.el_emerald_mine_club       = TRUE;
8372   si->editor.el_more                    = TRUE;
8373   si->editor.el_sokoban                 = TRUE;
8374   si->editor.el_supaplex                = TRUE;
8375   si->editor.el_diamond_caves           = TRUE;
8376   si->editor.el_dx_boulderdash          = TRUE;
8377   si->editor.el_chars                   = TRUE;
8378   si->editor.el_steel_chars             = TRUE;
8379
8380   si->editor.el_classic                 = TRUE;
8381   si->editor.el_custom                  = TRUE;
8382
8383   si->editor.el_user_defined            = FALSE;
8384   si->editor.el_dynamic                 = TRUE;
8385
8386   si->editor.el_headlines               = TRUE;
8387
8388   si->editor.show_element_token         = FALSE;
8389
8390   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
8391   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
8392   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
8393
8394   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
8395   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
8396   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
8397   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
8398   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8399
8400   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
8401   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
8402   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
8403   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
8404   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
8405   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
8406
8407   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
8408   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
8409   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
8410
8411   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
8412   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
8413   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
8414   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
8415
8416   for (i = 0; i < MAX_PLAYERS; i++)
8417   {
8418     si->input[i].use_joystick = FALSE;
8419     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8420     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
8421     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8422     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
8423     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
8424     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8425     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
8426     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
8427     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
8428     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
8429     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8430     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
8431     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
8432     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
8433     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
8434   }
8435
8436   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8437   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8438   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8439
8440   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
8441   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
8442   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
8443   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
8444   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8445   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
8446
8447   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8448
8449   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8450   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8451   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8452
8453   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8454   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
8455   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
8456
8457   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8458   si->internal.choose_from_top_leveldir = FALSE;
8459
8460   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
8461   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8462
8463   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8464   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8465   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8466   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8467   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8468   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8469   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8470   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8471   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8472   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8473
8474   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8475   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8476   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8477   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8478   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8479   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8480   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8481   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8482   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8483   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8484
8485   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8486   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
8487
8488   si->options.verbose = FALSE;
8489
8490 #if defined(PLATFORM_ANDROID)
8491   si->fullscreen = TRUE;
8492 #endif
8493 }
8494
8495 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8496 {
8497   si->editor_cascade.el_bd              = TRUE;
8498   si->editor_cascade.el_em              = TRUE;
8499   si->editor_cascade.el_emc             = TRUE;
8500   si->editor_cascade.el_rnd             = TRUE;
8501   si->editor_cascade.el_sb              = TRUE;
8502   si->editor_cascade.el_sp              = TRUE;
8503   si->editor_cascade.el_dc              = TRUE;
8504   si->editor_cascade.el_dx              = TRUE;
8505
8506   si->editor_cascade.el_chars           = FALSE;
8507   si->editor_cascade.el_steel_chars     = FALSE;
8508   si->editor_cascade.el_ce              = FALSE;
8509   si->editor_cascade.el_ge              = FALSE;
8510   si->editor_cascade.el_ref             = FALSE;
8511   si->editor_cascade.el_user            = FALSE;
8512   si->editor_cascade.el_dynamic         = FALSE;
8513 }
8514
8515 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8516 {
8517   int i, pnr;
8518
8519   if (!setup_file_hash)
8520     return;
8521
8522   /* global setup */
8523   si = setup;
8524   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8525     setSetupInfo(global_setup_tokens, i,
8526                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8527   setup = si;
8528
8529   /* editor setup */
8530   sei = setup.editor;
8531   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8532     setSetupInfo(editor_setup_tokens, i,
8533                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8534   setup.editor = sei;
8535
8536   /* shortcut setup */
8537   ssi = setup.shortcut;
8538   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8539     setSetupInfo(shortcut_setup_tokens, i,
8540                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8541   setup.shortcut = ssi;
8542
8543   /* player setup */
8544   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8545   {
8546     char prefix[30];
8547
8548     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8549
8550     sii = setup.input[pnr];
8551     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8552     {
8553       char full_token[100];
8554
8555       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8556       setSetupInfo(player_setup_tokens, i,
8557                    getHashEntry(setup_file_hash, full_token));
8558     }
8559     setup.input[pnr] = sii;
8560   }
8561
8562   /* system setup */
8563   syi = setup.system;
8564   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8565     setSetupInfo(system_setup_tokens, i,
8566                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8567   setup.system = syi;
8568
8569   /* internal setup */
8570   sxi = setup.internal;
8571   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8572     setSetupInfo(internal_setup_tokens, i,
8573                  getHashEntry(setup_file_hash, internal_setup_tokens[i].text));
8574   setup.internal = sxi;
8575
8576   /* debug setup */
8577   sdi = setup.debug;
8578   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8579     setSetupInfo(debug_setup_tokens, i,
8580                  getHashEntry(setup_file_hash, debug_setup_tokens[i].text));
8581   setup.debug = sdi;
8582
8583   /* options setup */
8584   soi = setup.options;
8585   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8586     setSetupInfo(options_setup_tokens, i,
8587                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8588   setup.options = soi;
8589 }
8590
8591 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8592 {
8593   int i;
8594
8595   if (!setup_file_hash)
8596     return;
8597
8598   /* editor cascade setup */
8599   seci = setup.editor_cascade;
8600   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8601     setSetupInfo(editor_cascade_setup_tokens, i,
8602                  getHashEntry(setup_file_hash,
8603                               editor_cascade_setup_tokens[i].text));
8604   setup.editor_cascade = seci;
8605 }
8606
8607 void LoadSetupFromFilename(char *filename)
8608 {
8609   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8610
8611   if (setup_file_hash)
8612   {
8613     decodeSetupFileHash(setup_file_hash);
8614
8615     freeSetupFileHash(setup_file_hash);
8616   }
8617   else
8618   {
8619     Error(ERR_WARN, "using default setup values");
8620   }
8621 }
8622
8623 static void LoadSetup_SpecialPostProcessing()
8624 {
8625   char *player_name_new;
8626
8627   /* needed to work around problems with fixed length strings */
8628   player_name_new = get_corrected_login_name(setup.player_name);
8629   free(setup.player_name);
8630   setup.player_name = player_name_new;
8631
8632   /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8633   if (setup.scroll_delay == FALSE)
8634   {
8635     setup.scroll_delay_value = MIN_SCROLL_DELAY;
8636     setup.scroll_delay = TRUE;                  /* now always "on" */
8637   }
8638
8639   /* make sure that scroll delay value stays inside valid range */
8640   setup.scroll_delay_value =
8641     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8642 }
8643
8644 void LoadSetup()
8645 {
8646   char *filename;
8647
8648   /* always start with reliable default values */
8649   setSetupInfoToDefaults(&setup);
8650
8651   /* try to load setup values from default setup file */
8652   filename = getDefaultSetupFilename();
8653
8654   if (fileExists(filename))
8655     LoadSetupFromFilename(filename);
8656
8657   /* try to load setup values from user setup file */
8658   filename = getSetupFilename();
8659
8660   LoadSetupFromFilename(filename);
8661
8662   LoadSetup_SpecialPostProcessing();
8663 }
8664
8665 void LoadSetup_EditorCascade()
8666 {
8667   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8668   SetupFileHash *setup_file_hash = NULL;
8669
8670   /* always start with reliable default values */
8671   setSetupInfoToDefaults_EditorCascade(&setup);
8672
8673   setup_file_hash = loadSetupFileHash(filename);
8674
8675   if (setup_file_hash)
8676   {
8677     decodeSetupFileHash_EditorCascade(setup_file_hash);
8678
8679     freeSetupFileHash(setup_file_hash);
8680   }
8681
8682   free(filename);
8683 }
8684
8685 void SaveSetup()
8686 {
8687   char *filename = getSetupFilename();
8688   FILE *file;
8689   int i, pnr;
8690
8691   InitUserDataDirectory();
8692
8693   if (!(file = fopen(filename, MODE_WRITE)))
8694   {
8695     Error(ERR_WARN, "cannot write setup file '%s'", filename);
8696     return;
8697   }
8698
8699   fprintFileHeader(file, SETUP_FILENAME);
8700
8701   /* global setup */
8702   si = setup;
8703   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8704   {
8705     /* just to make things nicer :) */
8706     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8707         i == SETUP_TOKEN_GRAPHICS_SET ||
8708         i == SETUP_TOKEN_VOLUME_SIMPLE ||
8709         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8710       fprintf(file, "\n");
8711
8712     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8713   }
8714
8715   /* editor setup */
8716   sei = setup.editor;
8717   fprintf(file, "\n");
8718   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8719     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8720
8721   /* shortcut setup */
8722   ssi = setup.shortcut;
8723   fprintf(file, "\n");
8724   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8725     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8726
8727   /* player setup */
8728   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8729   {
8730     char prefix[30];
8731
8732     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8733     fprintf(file, "\n");
8734
8735     sii = setup.input[pnr];
8736     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8737       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8738   }
8739
8740   /* system setup */
8741   syi = setup.system;
8742   fprintf(file, "\n");
8743   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8744     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8745
8746   /* internal setup */
8747   /* (internal setup values not saved to user setup file) */
8748
8749   /* debug setup */
8750   sdi = setup.debug;
8751   fprintf(file, "\n");
8752   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8753     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
8754
8755   /* options setup */
8756   soi = setup.options;
8757   fprintf(file, "\n");
8758   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8759     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8760
8761   fclose(file);
8762
8763   SetFilePermissions(filename, PERMS_PRIVATE);
8764 }
8765
8766 void SaveSetup_EditorCascade()
8767 {
8768   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8769   FILE *file;
8770   int i;
8771
8772   InitUserDataDirectory();
8773
8774   if (!(file = fopen(filename, MODE_WRITE)))
8775   {
8776     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8777     free(filename);
8778     return;
8779   }
8780
8781   fprintFileHeader(file, EDITORCASCADE_FILENAME);
8782
8783   seci = setup.editor_cascade;
8784   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8785     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8786
8787   fclose(file);
8788
8789   SetFilePermissions(filename, PERMS_PRIVATE);
8790
8791   free(filename);
8792 }
8793
8794 void LoadCustomElementDescriptions()
8795 {
8796   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8797   SetupFileHash *setup_file_hash;
8798   int i;
8799
8800   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8801   {
8802     if (element_info[i].custom_description != NULL)
8803     {
8804       free(element_info[i].custom_description);
8805       element_info[i].custom_description = NULL;
8806     }
8807   }
8808
8809   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8810     return;
8811
8812   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8813   {
8814     char *token = getStringCat2(element_info[i].token_name, ".name");
8815     char *value = getHashEntry(setup_file_hash, token);
8816
8817     if (value != NULL)
8818       element_info[i].custom_description = getStringCopy(value);
8819
8820     free(token);
8821   }
8822
8823   freeSetupFileHash(setup_file_hash);
8824 }
8825
8826 static int getElementFromToken(char *token)
8827 {
8828   char *value = getHashEntry(element_token_hash, token);
8829
8830   if (value != NULL)
8831     return atoi(value);
8832
8833   Error(ERR_WARN, "unknown element token '%s'", token);
8834
8835   return EL_UNDEFINED;
8836 }
8837
8838 static int get_token_parameter_value(char *token, char *value_raw)
8839 {
8840   char *suffix;
8841
8842   if (token == NULL || value_raw == NULL)
8843     return ARG_UNDEFINED_VALUE;
8844
8845   suffix = strrchr(token, '.');
8846   if (suffix == NULL)
8847     suffix = token;
8848
8849   if (strEqual(suffix, ".element"))
8850     return getElementFromToken(value_raw);
8851
8852   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
8853   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
8854 }
8855
8856 void InitMenuDesignSettings_Static()
8857 {
8858   int i;
8859
8860   /* always start with reliable default values from static default config */
8861   for (i = 0; image_config_vars[i].token != NULL; i++)
8862   {
8863     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
8864
8865     if (value != NULL)
8866       *image_config_vars[i].value =
8867         get_token_parameter_value(image_config_vars[i].token, value);
8868   }
8869 }
8870
8871 static void InitMenuDesignSettings_SpecialPreProcessing()
8872 {
8873   int i;
8874
8875   /* the following initializes hierarchical values from static configuration */
8876
8877   /* special case: initialize "ARG_DEFAULT" values in static default config */
8878   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
8879   titlescreen_initial_first_default.fade_mode  =
8880     title_initial_first_default.fade_mode;
8881   titlescreen_initial_first_default.fade_delay =
8882     title_initial_first_default.fade_delay;
8883   titlescreen_initial_first_default.post_delay =
8884     title_initial_first_default.post_delay;
8885   titlescreen_initial_first_default.auto_delay =
8886     title_initial_first_default.auto_delay;
8887   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
8888   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
8889   titlescreen_first_default.post_delay = title_first_default.post_delay;
8890   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
8891   titlemessage_initial_first_default.fade_mode  =
8892     title_initial_first_default.fade_mode;
8893   titlemessage_initial_first_default.fade_delay =
8894     title_initial_first_default.fade_delay;
8895   titlemessage_initial_first_default.post_delay =
8896     title_initial_first_default.post_delay;
8897   titlemessage_initial_first_default.auto_delay =
8898     title_initial_first_default.auto_delay;
8899   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
8900   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
8901   titlemessage_first_default.post_delay = title_first_default.post_delay;
8902   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
8903
8904   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
8905   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
8906   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
8907   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
8908   titlescreen_default.fade_mode  = title_default.fade_mode;
8909   titlescreen_default.fade_delay = title_default.fade_delay;
8910   titlescreen_default.post_delay = title_default.post_delay;
8911   titlescreen_default.auto_delay = title_default.auto_delay;
8912   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
8913   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
8914   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
8915   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
8916   titlemessage_default.fade_mode  = title_default.fade_mode;
8917   titlemessage_default.fade_delay = title_default.fade_delay;
8918   titlemessage_default.post_delay = title_default.post_delay;
8919   titlemessage_default.auto_delay = title_default.auto_delay;
8920
8921   /* special case: initialize "ARG_DEFAULT" values in static default config */
8922   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8923   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
8924   {
8925     titlescreen_initial_first[i] = titlescreen_initial_first_default;
8926     titlescreen_first[i] = titlescreen_first_default;
8927     titlemessage_initial_first[i] = titlemessage_initial_first_default;
8928     titlemessage_first[i] = titlemessage_first_default;
8929
8930     titlescreen_initial[i] = titlescreen_initial_default;
8931     titlescreen[i] = titlescreen_default;
8932     titlemessage_initial[i] = titlemessage_initial_default;
8933     titlemessage[i] = titlemessage_default;
8934   }
8935
8936   /* special case: initialize "ARG_DEFAULT" values in static default config */
8937   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8938   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8939   {
8940     if (i == GFX_SPECIAL_ARG_TITLE)     /* title values already initialized */
8941       continue;
8942
8943     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
8944     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
8945     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
8946   }
8947
8948   /* special case: initialize "ARG_DEFAULT" values in static default config */
8949   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8950   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8951   {
8952     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
8953     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
8954     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
8955
8956     if (i == GFX_SPECIAL_ARG_EDITOR)    /* editor values already initialized */
8957       continue;
8958
8959     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
8960   }
8961 }
8962
8963 static void InitMenuDesignSettings_SpecialPostProcessing()
8964 {
8965   static struct
8966   {
8967     struct XY *dst, *src;
8968   }
8969   game_buttons_xy[] =
8970   {
8971     { &game.button.save,        &game.button.stop       },
8972     { &game.button.pause2,      &game.button.pause      },
8973     { &game.button.load,        &game.button.play       },
8974     { &game.button.undo,        &game.button.stop       },
8975     { &game.button.redo,        &game.button.play       },
8976
8977     { NULL,                     NULL                    }
8978   };
8979   int i;
8980
8981   /* special case: initialize later added SETUP list size from LEVELS value */
8982   if (menu.list_size[GAME_MODE_SETUP] == -1)
8983     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
8984
8985   /* set default position for snapshot buttons to stop/pause/play buttons */
8986   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
8987     if ((*game_buttons_xy[i].dst).x == -1 &&
8988         (*game_buttons_xy[i].dst).y == -1)
8989       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
8990 }
8991
8992 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
8993 {
8994   static struct
8995   {
8996     struct XYTileSize *dst, *src;
8997     int graphic;
8998   }
8999   editor_buttons_xy[] =
9000   {
9001     {
9002       &editor.button.element_left,      &editor.palette.element_left,
9003       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9004     },
9005     {
9006       &editor.button.element_middle,    &editor.palette.element_middle,
9007       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9008     },
9009     {
9010       &editor.button.element_right,     &editor.palette.element_right,
9011       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9012     },
9013
9014     { NULL,                     NULL                    }
9015   };
9016   int i;
9017
9018   /* set default position for element buttons to element graphics */
9019   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9020   {
9021     if ((*editor_buttons_xy[i].dst).x == -1 &&
9022         (*editor_buttons_xy[i].dst).y == -1)
9023     {
9024       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9025
9026       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9027
9028       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9029     }
9030   }
9031 }
9032
9033 static void LoadMenuDesignSettingsFromFilename(char *filename)
9034 {
9035   static struct TitleFadingInfo tfi;
9036   static struct TitleMessageInfo tmi;
9037   static struct TokenInfo title_tokens[] =
9038   {
9039     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
9040     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
9041     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
9042     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
9043
9044     { -1,               NULL,                   NULL                    }
9045   };
9046   static struct TokenInfo titlemessage_tokens[] =
9047   {
9048     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9049     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9050     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9051     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9052     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9053     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9054     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9055     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9056     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9057     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9058     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9059     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9060     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9061     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9062     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9063     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9064     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9065
9066     { -1,               NULL,                   NULL                    }
9067   };
9068   static struct
9069   {
9070     struct TitleFadingInfo *info;
9071     char *text;
9072   }
9073   title_info[] =
9074   {
9075     /* initialize first titles from "enter screen" definitions, if defined */
9076     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
9077     { &title_first_default,             "menu.enter_screen.TITLE"       },
9078
9079     /* initialize title screens from "next screen" definitions, if defined */
9080     { &title_initial_default,           "menu.next_screen.TITLE"        },
9081     { &title_default,                   "menu.next_screen.TITLE"        },
9082
9083     { NULL,                             NULL                            }
9084   };
9085   static struct
9086   {
9087     struct TitleMessageInfo *array;
9088     char *text;
9089   }
9090   titlemessage_arrays[] =
9091   {
9092     /* initialize first titles from "enter screen" definitions, if defined */
9093     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
9094     { titlescreen_first,                "menu.enter_screen.TITLE"       },
9095     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
9096     { titlemessage_first,               "menu.enter_screen.TITLE"       },
9097
9098     /* initialize titles from "next screen" definitions, if defined */
9099     { titlescreen_initial,              "menu.next_screen.TITLE"        },
9100     { titlescreen,                      "menu.next_screen.TITLE"        },
9101     { titlemessage_initial,             "menu.next_screen.TITLE"        },
9102     { titlemessage,                     "menu.next_screen.TITLE"        },
9103
9104     /* overwrite titles with title definitions, if defined */
9105     { titlescreen_initial_first,        "[title_initial]"               },
9106     { titlescreen_first,                "[title]"                       },
9107     { titlemessage_initial_first,       "[title_initial]"               },
9108     { titlemessage_first,               "[title]"                       },
9109
9110     { titlescreen_initial,              "[title_initial]"               },
9111     { titlescreen,                      "[title]"                       },
9112     { titlemessage_initial,             "[title_initial]"               },
9113     { titlemessage,                     "[title]"                       },
9114
9115     /* overwrite titles with title screen/message definitions, if defined */
9116     { titlescreen_initial_first,        "[titlescreen_initial]"         },
9117     { titlescreen_first,                "[titlescreen]"                 },
9118     { titlemessage_initial_first,       "[titlemessage_initial]"        },
9119     { titlemessage_first,               "[titlemessage]"                },
9120
9121     { titlescreen_initial,              "[titlescreen_initial]"         },
9122     { titlescreen,                      "[titlescreen]"                 },
9123     { titlemessage_initial,             "[titlemessage_initial]"        },
9124     { titlemessage,                     "[titlemessage]"                },
9125
9126     { NULL,                             NULL                            }
9127   };
9128   SetupFileHash *setup_file_hash;
9129   int i, j, k;
9130
9131   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9132     return;
9133
9134   /* the following initializes hierarchical values from dynamic configuration */
9135
9136   /* special case: initialize with default values that may be overwritten */
9137   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9138   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9139   {
9140     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9141     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9142     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9143
9144     if (value_1 != NULL)
9145       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9146     if (value_2 != NULL)
9147       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9148     if (value_3 != NULL)
9149       menu.list_size[i] = get_integer_from_string(value_3);
9150   }
9151
9152   /* special case: initialize with default values that may be overwritten */
9153   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9154   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9155   {
9156     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9157     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9158
9159     if (value_1 != NULL)
9160       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9161     if (value_2 != NULL)
9162       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9163
9164     if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9165     {
9166       char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9167
9168       if (value_1 != NULL)
9169         menu.list_size_info[i] = get_integer_from_string(value_1);
9170     }
9171   }
9172
9173   /* special case: initialize with default values that may be overwritten */
9174   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9175   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9176   {
9177     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9178     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9179
9180     if (value_1 != NULL)
9181       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9182     if (value_2 != NULL)
9183       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9184   }
9185
9186   /* special case: initialize with default values that may be overwritten */
9187   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9188   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9189   {
9190     char *token_1 = "menu.enter_screen.fade_mode";
9191     char *token_2 = "menu.enter_screen.fade_delay";
9192     char *token_3 = "menu.enter_screen.post_delay";
9193     char *token_4 = "menu.leave_screen.fade_mode";
9194     char *token_5 = "menu.leave_screen.fade_delay";
9195     char *token_6 = "menu.leave_screen.post_delay";
9196     char *token_7 = "menu.next_screen.fade_mode";
9197     char *token_8 = "menu.next_screen.fade_delay";
9198     char *token_9 = "menu.next_screen.post_delay";
9199     char *value_1 = getHashEntry(setup_file_hash, token_1);
9200     char *value_2 = getHashEntry(setup_file_hash, token_2);
9201     char *value_3 = getHashEntry(setup_file_hash, token_3);
9202     char *value_4 = getHashEntry(setup_file_hash, token_4);
9203     char *value_5 = getHashEntry(setup_file_hash, token_5);
9204     char *value_6 = getHashEntry(setup_file_hash, token_6);
9205     char *value_7 = getHashEntry(setup_file_hash, token_7);
9206     char *value_8 = getHashEntry(setup_file_hash, token_8);
9207     char *value_9 = getHashEntry(setup_file_hash, token_9);
9208
9209     if (value_1 != NULL)
9210       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9211                                                                  value_1);
9212     if (value_2 != NULL)
9213       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9214                                                                   value_2);
9215     if (value_3 != NULL)
9216       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9217                                                                   value_3);
9218     if (value_4 != NULL)
9219       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9220                                                                  value_4);
9221     if (value_5 != NULL)
9222       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9223                                                                   value_5);
9224     if (value_6 != NULL)
9225       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9226                                                                   value_6);
9227     if (value_7 != NULL)
9228       menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9229                                                                 value_7);
9230     if (value_8 != NULL)
9231       menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9232                                                                  value_8);
9233     if (value_9 != NULL)
9234       menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9235                                                                  value_9);
9236   }
9237
9238   /* special case: initialize with default values that may be overwritten */
9239   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9240   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9241   {
9242     char *token_w1 = "viewport.window.width";
9243     char *token_w2 = "viewport.window.height";
9244     char *token_01 = "viewport.playfield.x";
9245     char *token_02 = "viewport.playfield.y";
9246     char *token_03 = "viewport.playfield.width";
9247     char *token_04 = "viewport.playfield.height";
9248     char *token_05 = "viewport.playfield.border_size";
9249     char *token_06 = "viewport.door_1.x";
9250     char *token_07 = "viewport.door_1.y";
9251     char *token_08 = "viewport.door_1.width";
9252     char *token_09 = "viewport.door_1.height";
9253     char *token_10 = "viewport.door_1.border_size";
9254     char *token_11 = "viewport.door_2.x";
9255     char *token_12 = "viewport.door_2.y";
9256     char *token_13 = "viewport.door_2.width";
9257     char *token_14 = "viewport.door_2.height";
9258     char *token_15 = "viewport.door_2.border_size";
9259     char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9260     char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9261     char *value_01 = getHashEntry(setup_file_hash, token_01);
9262     char *value_02 = getHashEntry(setup_file_hash, token_02);
9263     char *value_03 = getHashEntry(setup_file_hash, token_03);
9264     char *value_04 = getHashEntry(setup_file_hash, token_04);
9265     char *value_05 = getHashEntry(setup_file_hash, token_05);
9266     char *value_06 = getHashEntry(setup_file_hash, token_06);
9267     char *value_07 = getHashEntry(setup_file_hash, token_07);
9268     char *value_08 = getHashEntry(setup_file_hash, token_08);
9269     char *value_09 = getHashEntry(setup_file_hash, token_09);
9270     char *value_10 = getHashEntry(setup_file_hash, token_10);
9271     char *value_11 = getHashEntry(setup_file_hash, token_11);
9272     char *value_12 = getHashEntry(setup_file_hash, token_12);
9273     char *value_13 = getHashEntry(setup_file_hash, token_13);
9274     char *value_14 = getHashEntry(setup_file_hash, token_14);
9275     char *value_15 = getHashEntry(setup_file_hash, token_15);
9276
9277     if (value_w1 != NULL)
9278       viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9279     if (value_w2 != NULL)
9280       viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9281     if (value_01 != NULL)
9282       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9283     if (value_02 != NULL)
9284       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9285     if (value_03 != NULL)
9286       viewport.playfield[i].width = get_token_parameter_value(token_03,
9287                                                               value_03);
9288     if (value_04 != NULL)
9289       viewport.playfield[i].height = get_token_parameter_value(token_04,
9290                                                                value_04);
9291     if (value_05 != NULL)
9292       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9293                                                                     value_05);
9294     if (value_06 != NULL)
9295       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9296     if (value_07 != NULL)
9297       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9298     if (value_08 != NULL)
9299       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9300     if (value_09 != NULL)
9301       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9302     if (value_10 != NULL)
9303       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9304                                                                  value_10);
9305     if (value_11 != NULL)
9306       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9307     if (value_12 != NULL)
9308       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9309     if (value_13 != NULL)
9310       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9311     if (value_14 != NULL)
9312       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9313     if (value_15 != NULL)
9314       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9315                                                                  value_15);
9316   }
9317
9318   /* special case: initialize with default values that may be overwritten */
9319   /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9320   for (i = 0; title_info[i].info != NULL; i++)
9321   {
9322     struct TitleFadingInfo *info = title_info[i].info;
9323     char *base_token = title_info[i].text;
9324
9325     for (j = 0; title_tokens[j].type != -1; j++)
9326     {
9327       char *token = getStringCat2(base_token, title_tokens[j].text);
9328       char *value = getHashEntry(setup_file_hash, token);
9329
9330       if (value != NULL)
9331       {
9332         int parameter_value = get_token_parameter_value(token, value);
9333
9334         tfi = *info;
9335
9336         *(int *)title_tokens[j].value = (int)parameter_value;
9337
9338         *info = tfi;
9339       }
9340
9341       free(token);
9342     }
9343   }
9344
9345   /* special case: initialize with default values that may be overwritten */
9346   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9347   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9348   {
9349     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9350     char *base_token = titlemessage_arrays[i].text;
9351
9352     for (j = 0; titlemessage_tokens[j].type != -1; j++)
9353     {
9354       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9355       char *value = getHashEntry(setup_file_hash, token);
9356
9357       if (value != NULL)
9358       {
9359         int parameter_value = get_token_parameter_value(token, value);
9360
9361         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9362         {
9363           tmi = array[k];
9364
9365           if (titlemessage_tokens[j].type == TYPE_INTEGER)
9366             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
9367           else
9368             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9369
9370           array[k] = tmi;
9371         }
9372       }
9373
9374       free(token);
9375     }
9376   }
9377
9378   /* read (and overwrite with) values that may be specified in config file */
9379   for (i = 0; image_config_vars[i].token != NULL; i++)
9380   {
9381     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9382
9383     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9384     if (value != NULL && !strEqual(value, ARG_DEFAULT))
9385       *image_config_vars[i].value =
9386         get_token_parameter_value(image_config_vars[i].token, value);
9387   }
9388
9389   freeSetupFileHash(setup_file_hash);
9390 }
9391
9392 void LoadMenuDesignSettings()
9393 {
9394   char *filename_base = UNDEFINED_FILENAME, *filename_local;
9395
9396   InitMenuDesignSettings_Static();
9397   InitMenuDesignSettings_SpecialPreProcessing();
9398
9399   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9400   {
9401     /* first look for special settings configured in level series config */
9402     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9403
9404     if (fileExists(filename_base))
9405       LoadMenuDesignSettingsFromFilename(filename_base);
9406   }
9407
9408   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9409
9410   if (filename_local != NULL && !strEqual(filename_base, filename_local))
9411     LoadMenuDesignSettingsFromFilename(filename_local);
9412
9413   InitMenuDesignSettings_SpecialPostProcessing();
9414 }
9415
9416 void LoadMenuDesignSettings_AfterGraphics()
9417 {
9418   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9419 }
9420
9421 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9422 {
9423   char *filename = getEditorSetupFilename();
9424   SetupFileList *setup_file_list, *list;
9425   SetupFileHash *element_hash;
9426   int num_unknown_tokens = 0;
9427   int i;
9428
9429   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9430     return;
9431
9432   element_hash = newSetupFileHash();
9433
9434   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9435     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9436
9437   /* determined size may be larger than needed (due to unknown elements) */
9438   *num_elements = 0;
9439   for (list = setup_file_list; list != NULL; list = list->next)
9440     (*num_elements)++;
9441
9442   /* add space for up to 3 more elements for padding that may be needed */
9443   *num_elements += 3;
9444
9445   /* free memory for old list of elements, if needed */
9446   checked_free(*elements);
9447
9448   /* allocate memory for new list of elements */
9449   *elements = checked_malloc(*num_elements * sizeof(int));
9450
9451   *num_elements = 0;
9452   for (list = setup_file_list; list != NULL; list = list->next)
9453   {
9454     char *value = getHashEntry(element_hash, list->token);
9455
9456     if (value == NULL)          /* try to find obsolete token mapping */
9457     {
9458       char *mapped_token = get_mapped_token(list->token);
9459
9460       if (mapped_token != NULL)
9461       {
9462         value = getHashEntry(element_hash, mapped_token);
9463
9464         free(mapped_token);
9465       }
9466     }
9467
9468     if (value != NULL)
9469     {
9470       (*elements)[(*num_elements)++] = atoi(value);
9471     }
9472     else
9473     {
9474       if (num_unknown_tokens == 0)
9475       {
9476         Error(ERR_INFO_LINE, "-");
9477         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9478         Error(ERR_INFO, "- config file: '%s'", filename);
9479
9480         num_unknown_tokens++;
9481       }
9482
9483       Error(ERR_INFO, "- token: '%s'", list->token);
9484     }
9485   }
9486
9487   if (num_unknown_tokens > 0)
9488     Error(ERR_INFO_LINE, "-");
9489
9490   while (*num_elements % 4)     /* pad with empty elements, if needed */
9491     (*elements)[(*num_elements)++] = EL_EMPTY;
9492
9493   freeSetupFileList(setup_file_list);
9494   freeSetupFileHash(element_hash);
9495
9496 #if 0
9497   for (i = 0; i < *num_elements; i++)
9498     printf("editor: element '%s' [%d]\n",
9499            element_info[(*elements)[i]].token_name, (*elements)[i]);
9500 #endif
9501 }
9502
9503 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9504                                                      boolean is_sound)
9505 {
9506   SetupFileHash *setup_file_hash = NULL;
9507   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9508   char *filename_music, *filename_prefix, *filename_info;
9509   struct
9510   {
9511     char *token;
9512     char **value_ptr;
9513   }
9514   token_to_value_ptr[] =
9515   {
9516     { "title_header",   &tmp_music_file_info.title_header       },
9517     { "artist_header",  &tmp_music_file_info.artist_header      },
9518     { "album_header",   &tmp_music_file_info.album_header       },
9519     { "year_header",    &tmp_music_file_info.year_header        },
9520
9521     { "title",          &tmp_music_file_info.title              },
9522     { "artist",         &tmp_music_file_info.artist             },
9523     { "album",          &tmp_music_file_info.album              },
9524     { "year",           &tmp_music_file_info.year               },
9525
9526     { NULL,             NULL                                    },
9527   };
9528   int i;
9529
9530   filename_music = (is_sound ? getCustomSoundFilename(basename) :
9531                     getCustomMusicFilename(basename));
9532
9533   if (filename_music == NULL)
9534     return NULL;
9535
9536   /* ---------- try to replace file extension ---------- */
9537
9538   filename_prefix = getStringCopy(filename_music);
9539   if (strrchr(filename_prefix, '.') != NULL)
9540     *strrchr(filename_prefix, '.') = '\0';
9541   filename_info = getStringCat2(filename_prefix, ".txt");
9542
9543   if (fileExists(filename_info))
9544     setup_file_hash = loadSetupFileHash(filename_info);
9545
9546   free(filename_prefix);
9547   free(filename_info);
9548
9549   if (setup_file_hash == NULL)
9550   {
9551     /* ---------- try to add file extension ---------- */
9552
9553     filename_prefix = getStringCopy(filename_music);
9554     filename_info = getStringCat2(filename_prefix, ".txt");
9555
9556     if (fileExists(filename_info))
9557       setup_file_hash = loadSetupFileHash(filename_info);
9558
9559     free(filename_prefix);
9560     free(filename_info);
9561   }
9562
9563   if (setup_file_hash == NULL)
9564     return NULL;
9565
9566   /* ---------- music file info found ---------- */
9567
9568   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9569
9570   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9571   {
9572     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9573
9574     *token_to_value_ptr[i].value_ptr =
9575       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9576   }
9577
9578   tmp_music_file_info.basename = getStringCopy(basename);
9579   tmp_music_file_info.music = music;
9580   tmp_music_file_info.is_sound = is_sound;
9581
9582   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9583   *new_music_file_info = tmp_music_file_info;
9584
9585   return new_music_file_info;
9586 }
9587
9588 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9589 {
9590   return get_music_file_info_ext(basename, music, FALSE);
9591 }
9592
9593 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9594 {
9595   return get_music_file_info_ext(basename, sound, TRUE);
9596 }
9597
9598 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9599                                      char *basename, boolean is_sound)
9600 {
9601   for (; list != NULL; list = list->next)
9602     if (list->is_sound == is_sound && strEqual(list->basename, basename))
9603       return TRUE;
9604
9605   return FALSE;
9606 }
9607
9608 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9609 {
9610   return music_info_listed_ext(list, basename, FALSE);
9611 }
9612
9613 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9614 {
9615   return music_info_listed_ext(list, basename, TRUE);
9616 }
9617
9618 void LoadMusicInfo()
9619 {
9620   char *music_directory = getCustomMusicDirectory();
9621   int num_music = getMusicListSize();
9622   int num_music_noconf = 0;
9623   int num_sounds = getSoundListSize();
9624   Directory *dir;
9625   DirectoryEntry *dir_entry;
9626   struct FileInfo *music, *sound;
9627   struct MusicFileInfo *next, **new;
9628   int i;
9629
9630   while (music_file_info != NULL)
9631   {
9632     next = music_file_info->next;
9633
9634     checked_free(music_file_info->basename);
9635
9636     checked_free(music_file_info->title_header);
9637     checked_free(music_file_info->artist_header);
9638     checked_free(music_file_info->album_header);
9639     checked_free(music_file_info->year_header);
9640
9641     checked_free(music_file_info->title);
9642     checked_free(music_file_info->artist);
9643     checked_free(music_file_info->album);
9644     checked_free(music_file_info->year);
9645
9646     free(music_file_info);
9647
9648     music_file_info = next;
9649   }
9650
9651   new = &music_file_info;
9652
9653   for (i = 0; i < num_music; i++)
9654   {
9655     music = getMusicListEntry(i);
9656
9657     if (music->filename == NULL)
9658       continue;
9659
9660     if (strEqual(music->filename, UNDEFINED_FILENAME))
9661       continue;
9662
9663     /* a configured file may be not recognized as music */
9664     if (!FileIsMusic(music->filename))
9665       continue;
9666
9667     if (!music_info_listed(music_file_info, music->filename))
9668     {
9669       *new = get_music_file_info(music->filename, i);
9670
9671       if (*new != NULL)
9672         new = &(*new)->next;
9673     }
9674   }
9675
9676   if ((dir = openDirectory(music_directory)) == NULL)
9677   {
9678     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9679     return;
9680   }
9681
9682   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
9683   {
9684     char *basename = dir_entry->basename;
9685     boolean music_already_used = FALSE;
9686     int i;
9687
9688     /* skip all music files that are configured in music config file */
9689     for (i = 0; i < num_music; i++)
9690     {
9691       music = getMusicListEntry(i);
9692
9693       if (music->filename == NULL)
9694         continue;
9695
9696       if (strEqual(basename, music->filename))
9697       {
9698         music_already_used = TRUE;
9699         break;
9700       }
9701     }
9702
9703     if (music_already_used)
9704       continue;
9705
9706     if (!FileIsMusic(dir_entry->filename))
9707       continue;
9708
9709     if (!music_info_listed(music_file_info, basename))
9710     {
9711       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9712
9713       if (*new != NULL)
9714         new = &(*new)->next;
9715     }
9716
9717     num_music_noconf++;
9718   }
9719
9720   closeDirectory(dir);
9721
9722   for (i = 0; i < num_sounds; i++)
9723   {
9724     sound = getSoundListEntry(i);
9725
9726     if (sound->filename == NULL)
9727       continue;
9728
9729     if (strEqual(sound->filename, UNDEFINED_FILENAME))
9730       continue;
9731
9732     /* a configured file may be not recognized as sound */
9733     if (!FileIsSound(sound->filename))
9734       continue;
9735
9736     if (!sound_info_listed(music_file_info, sound->filename))
9737     {
9738       *new = get_sound_file_info(sound->filename, i);
9739       if (*new != NULL)
9740         new = &(*new)->next;
9741     }
9742   }
9743 }
9744
9745 void add_helpanim_entry(int element, int action, int direction, int delay,
9746                         int *num_list_entries)
9747 {
9748   struct HelpAnimInfo *new_list_entry;
9749   (*num_list_entries)++;
9750
9751   helpanim_info =
9752     checked_realloc(helpanim_info,
9753                     *num_list_entries * sizeof(struct HelpAnimInfo));
9754   new_list_entry = &helpanim_info[*num_list_entries - 1];
9755
9756   new_list_entry->element = element;
9757   new_list_entry->action = action;
9758   new_list_entry->direction = direction;
9759   new_list_entry->delay = delay;
9760 }
9761
9762 void print_unknown_token(char *filename, char *token, int token_nr)
9763 {
9764   if (token_nr == 0)
9765   {
9766     Error(ERR_INFO_LINE, "-");
9767     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9768     Error(ERR_INFO, "- config file: '%s'", filename);
9769   }
9770
9771   Error(ERR_INFO, "- token: '%s'", token);
9772 }
9773
9774 void print_unknown_token_end(int token_nr)
9775 {
9776   if (token_nr > 0)
9777     Error(ERR_INFO_LINE, "-");
9778 }
9779
9780 void LoadHelpAnimInfo()
9781 {
9782   char *filename = getHelpAnimFilename();
9783   SetupFileList *setup_file_list = NULL, *list;
9784   SetupFileHash *element_hash, *action_hash, *direction_hash;
9785   int num_list_entries = 0;
9786   int num_unknown_tokens = 0;
9787   int i;
9788
9789   if (fileExists(filename))
9790     setup_file_list = loadSetupFileList(filename);
9791
9792   if (setup_file_list == NULL)
9793   {
9794     /* use reliable default values from static configuration */
9795     SetupFileList *insert_ptr;
9796
9797     insert_ptr = setup_file_list =
9798       newSetupFileList(helpanim_config[0].token,
9799                        helpanim_config[0].value);
9800
9801     for (i = 1; helpanim_config[i].token; i++)
9802       insert_ptr = addListEntry(insert_ptr,
9803                                 helpanim_config[i].token,
9804                                 helpanim_config[i].value);
9805   }
9806
9807   element_hash   = newSetupFileHash();
9808   action_hash    = newSetupFileHash();
9809   direction_hash = newSetupFileHash();
9810
9811   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9812     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9813
9814   for (i = 0; i < NUM_ACTIONS; i++)
9815     setHashEntry(action_hash, element_action_info[i].suffix,
9816                  i_to_a(element_action_info[i].value));
9817
9818   /* do not store direction index (bit) here, but direction value! */
9819   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
9820     setHashEntry(direction_hash, element_direction_info[i].suffix,
9821                  i_to_a(1 << element_direction_info[i].value));
9822
9823   for (list = setup_file_list; list != NULL; list = list->next)
9824   {
9825     char *element_token, *action_token, *direction_token;
9826     char *element_value, *action_value, *direction_value;
9827     int delay = atoi(list->value);
9828
9829     if (strEqual(list->token, "end"))
9830     {
9831       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9832
9833       continue;
9834     }
9835
9836     /* first try to break element into element/action/direction parts;
9837        if this does not work, also accept combined "element[.act][.dir]"
9838        elements (like "dynamite.active"), which are unique elements */
9839
9840     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
9841     {
9842       element_value = getHashEntry(element_hash, list->token);
9843       if (element_value != NULL)        /* element found */
9844         add_helpanim_entry(atoi(element_value), -1, -1, delay,
9845                            &num_list_entries);
9846       else
9847       {
9848         /* no further suffixes found -- this is not an element */
9849         print_unknown_token(filename, list->token, num_unknown_tokens++);
9850       }
9851
9852       continue;
9853     }
9854
9855     /* token has format "<prefix>.<something>" */
9856
9857     action_token = strchr(list->token, '.');    /* suffix may be action ... */
9858     direction_token = action_token;             /* ... or direction */
9859
9860     element_token = getStringCopy(list->token);
9861     *strchr(element_token, '.') = '\0';
9862
9863     element_value = getHashEntry(element_hash, element_token);
9864
9865     if (element_value == NULL)          /* this is no element */
9866     {
9867       element_value = getHashEntry(element_hash, list->token);
9868       if (element_value != NULL)        /* combined element found */
9869         add_helpanim_entry(atoi(element_value), -1, -1, delay,
9870                            &num_list_entries);
9871       else
9872         print_unknown_token(filename, list->token, num_unknown_tokens++);
9873
9874       free(element_token);
9875
9876       continue;
9877     }
9878
9879     action_value = getHashEntry(action_hash, action_token);
9880
9881     if (action_value != NULL)           /* action found */
9882     {
9883       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
9884                     &num_list_entries);
9885
9886       free(element_token);
9887
9888       continue;
9889     }
9890
9891     direction_value = getHashEntry(direction_hash, direction_token);
9892
9893     if (direction_value != NULL)        /* direction found */
9894     {
9895       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
9896                          &num_list_entries);
9897
9898       free(element_token);
9899
9900       continue;
9901     }
9902
9903     if (strchr(action_token + 1, '.') == NULL)
9904     {
9905       /* no further suffixes found -- this is not an action nor direction */
9906
9907       element_value = getHashEntry(element_hash, list->token);
9908       if (element_value != NULL)        /* combined element found */
9909         add_helpanim_entry(atoi(element_value), -1, -1, delay,
9910                            &num_list_entries);
9911       else
9912         print_unknown_token(filename, list->token, num_unknown_tokens++);
9913
9914       free(element_token);
9915
9916       continue;
9917     }
9918
9919     /* token has format "<prefix>.<suffix>.<something>" */
9920
9921     direction_token = strchr(action_token + 1, '.');
9922
9923     action_token = getStringCopy(action_token);
9924     *strchr(action_token + 1, '.') = '\0';
9925
9926     action_value = getHashEntry(action_hash, action_token);
9927
9928     if (action_value == NULL)           /* this is no action */
9929     {
9930       element_value = getHashEntry(element_hash, list->token);
9931       if (element_value != NULL)        /* combined element found */
9932         add_helpanim_entry(atoi(element_value), -1, -1, delay,
9933                            &num_list_entries);
9934       else
9935         print_unknown_token(filename, list->token, num_unknown_tokens++);
9936
9937       free(element_token);
9938       free(action_token);
9939
9940       continue;
9941     }
9942
9943     direction_value = getHashEntry(direction_hash, direction_token);
9944
9945     if (direction_value != NULL)        /* direction found */
9946     {
9947       add_helpanim_entry(atoi(element_value), atoi(action_value),
9948                          atoi(direction_value), delay, &num_list_entries);
9949
9950       free(element_token);
9951       free(action_token);
9952
9953       continue;
9954     }
9955
9956     /* this is no direction */
9957
9958     element_value = getHashEntry(element_hash, list->token);
9959     if (element_value != NULL)          /* combined element found */
9960       add_helpanim_entry(atoi(element_value), -1, -1, delay,
9961                          &num_list_entries);
9962     else
9963       print_unknown_token(filename, list->token, num_unknown_tokens++);
9964
9965     free(element_token);
9966     free(action_token);
9967   }
9968
9969   print_unknown_token_end(num_unknown_tokens);
9970
9971   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9972   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
9973
9974   freeSetupFileList(setup_file_list);
9975   freeSetupFileHash(element_hash);
9976   freeSetupFileHash(action_hash);
9977   freeSetupFileHash(direction_hash);
9978
9979 #if 0
9980   for (i = 0; i < num_list_entries; i++)
9981     printf("::: '%s': %d, %d, %d => %d\n",
9982            EL_NAME(helpanim_info[i].element),
9983            helpanim_info[i].element,
9984            helpanim_info[i].action,
9985            helpanim_info[i].direction,
9986            helpanim_info[i].delay);
9987 #endif
9988 }
9989
9990 void LoadHelpTextInfo()
9991 {
9992   char *filename = getHelpTextFilename();
9993   int i;
9994
9995   if (helptext_info != NULL)
9996   {
9997     freeSetupFileHash(helptext_info);
9998     helptext_info = NULL;
9999   }
10000
10001   if (fileExists(filename))
10002     helptext_info = loadSetupFileHash(filename);
10003
10004   if (helptext_info == NULL)
10005   {
10006     /* use reliable default values from static configuration */
10007     helptext_info = newSetupFileHash();
10008
10009     for (i = 0; helptext_config[i].token; i++)
10010       setHashEntry(helptext_info,
10011                    helptext_config[i].token,
10012                    helptext_config[i].value);
10013   }
10014
10015 #if 0
10016   BEGIN_HASH_ITERATION(helptext_info, itr)
10017   {
10018     printf("::: '%s' => '%s'\n",
10019            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10020   }
10021   END_HASH_ITERATION(hash, itr)
10022 #endif
10023 }
10024
10025
10026 /* ------------------------------------------------------------------------- */
10027 /* convert levels                                                            */
10028 /* ------------------------------------------------------------------------- */
10029
10030 #define MAX_NUM_CONVERT_LEVELS          1000
10031
10032 void ConvertLevels()
10033 {
10034   static LevelDirTree *convert_leveldir = NULL;
10035   static int convert_level_nr = -1;
10036   static int num_levels_handled = 0;
10037   static int num_levels_converted = 0;
10038   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10039   int i;
10040
10041   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10042                                                global.convert_leveldir);
10043
10044   if (convert_leveldir == NULL)
10045     Error(ERR_EXIT, "no such level identifier: '%s'",
10046           global.convert_leveldir);
10047
10048   leveldir_current = convert_leveldir;
10049
10050   if (global.convert_level_nr != -1)
10051   {
10052     convert_leveldir->first_level = global.convert_level_nr;
10053     convert_leveldir->last_level  = global.convert_level_nr;
10054   }
10055
10056   convert_level_nr = convert_leveldir->first_level;
10057
10058   PrintLine("=", 79);
10059   Print("Converting levels\n");
10060   PrintLine("-", 79);
10061   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10062   Print("Level series name:       '%s'\n", convert_leveldir->name);
10063   Print("Level series author:     '%s'\n", convert_leveldir->author);
10064   Print("Number of levels:        %d\n",   convert_leveldir->levels);
10065   PrintLine("=", 79);
10066   Print("\n");
10067
10068   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10069     levels_failed[i] = FALSE;
10070
10071   while (convert_level_nr <= convert_leveldir->last_level)
10072   {
10073     char *level_filename;
10074     boolean new_level;
10075
10076     level_nr = convert_level_nr++;
10077
10078     Print("Level %03d: ", level_nr);
10079
10080     LoadLevel(level_nr);
10081     if (level.no_level_file || level.no_valid_file)
10082     {
10083       Print("(no level)\n");
10084       continue;
10085     }
10086
10087     Print("converting level ... ");
10088
10089     level_filename = getDefaultLevelFilename(level_nr);
10090     new_level = !fileExists(level_filename);
10091
10092     if (new_level)
10093     {
10094       SaveLevel(level_nr);
10095
10096       num_levels_converted++;
10097
10098       Print("converted.\n");
10099     }
10100     else
10101     {
10102       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10103         levels_failed[level_nr] = TRUE;
10104
10105       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10106     }
10107
10108     num_levels_handled++;
10109   }
10110
10111   Print("\n");
10112   PrintLine("=", 79);
10113   Print("Number of levels handled: %d\n", num_levels_handled);
10114   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10115          (num_levels_handled ?
10116           num_levels_converted * 100 / num_levels_handled : 0));
10117   PrintLine("-", 79);
10118   Print("Summary (for automatic parsing by scripts):\n");
10119   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10120          convert_leveldir->identifier, num_levels_converted,
10121          num_levels_handled,
10122          (num_levels_handled ?
10123           num_levels_converted * 100 / num_levels_handled : 0));
10124
10125   if (num_levels_handled != num_levels_converted)
10126   {
10127     Print(", FAILED:");
10128     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10129       if (levels_failed[i])
10130         Print(" %03d", i);
10131   }
10132
10133   Print("\n");
10134   PrintLine("=", 79);
10135
10136   CloseAllAndExit(0);
10137 }
10138
10139
10140 /* ------------------------------------------------------------------------- */
10141 /* create and save images for use in level sketches (raw BMP format)         */
10142 /* ------------------------------------------------------------------------- */
10143
10144 void CreateLevelSketchImages()
10145 {
10146 #if defined(TARGET_SDL)
10147   Bitmap *bitmap1;
10148   Bitmap *bitmap2;
10149   int i;
10150
10151   InitElementPropertiesGfxElement();
10152
10153   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10154   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10155
10156   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10157   {
10158     Bitmap *src_bitmap;
10159     int src_x, src_y;
10160     int element = getMappedElement(i);
10161     int graphic = el2edimg(element);
10162     char basename1[16];
10163     char basename2[16];
10164     char *filename1;
10165     char *filename2;
10166
10167     sprintf(basename1, "%03d.bmp", i);
10168     sprintf(basename2, "%03ds.bmp", i);
10169
10170     filename1 = getPath2(global.create_images_dir, basename1);
10171     filename2 = getPath2(global.create_images_dir, basename2);
10172
10173     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10174     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10175                0, 0);
10176
10177     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10178       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10179
10180     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10181     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10182
10183     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10184       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10185
10186     free(filename1);
10187     free(filename2);
10188
10189     if (options.debug)
10190       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10191   }
10192
10193   FreeBitmap(bitmap1);
10194   FreeBitmap(bitmap2);
10195
10196   if (options.debug)
10197     printf("\n");
10198
10199   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10200
10201   CloseAllAndExit(0);
10202 #endif
10203 }
10204
10205
10206 /* ------------------------------------------------------------------------- */
10207 /* create and save images for custom and group elements (raw BMP format)     */
10208 /* ------------------------------------------------------------------------- */
10209
10210 void CreateCustomElementImages(char *directory)
10211 {
10212 #if defined(TARGET_SDL)
10213   char *src_basename = "RocksCE-template.ilbm";
10214   char *dst_basename = "RocksCE.bmp";
10215   char *src_filename = getPath2(directory, src_basename);
10216   char *dst_filename = getPath2(directory, dst_basename);
10217   Bitmap *src_bitmap;
10218   Bitmap *bitmap;
10219   int yoffset_ce = 0;
10220   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10221   int i;
10222
10223   SDLInitVideoDisplay();
10224
10225   ReCreateBitmap(&backbuffer, video.width, video.height);
10226
10227   src_bitmap = LoadImage(src_filename);
10228
10229   bitmap = CreateBitmap(TILEX * 16 * 2,
10230                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10231                         DEFAULT_DEPTH);
10232
10233   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10234   {
10235     int x = i % 16;
10236     int y = i / 16;
10237     int ii = i + 1;
10238     int j;
10239
10240     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10241                TILEX * x, TILEY * y + yoffset_ce);
10242
10243     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10244                TILEX, TILEY,
10245                TILEX * x + TILEX * 16,
10246                TILEY * y + yoffset_ce);
10247
10248     for (j = 2; j >= 0; j--)
10249     {
10250       int c = ii % 10;
10251
10252       BlitBitmap(src_bitmap, bitmap,
10253                  TILEX + c * 7, 0, 6, 10,
10254                  TILEX * x + 6 + j * 7,
10255                  TILEY * y + 11 + yoffset_ce);
10256
10257       BlitBitmap(src_bitmap, bitmap,
10258                  TILEX + c * 8, TILEY, 6, 10,
10259                  TILEX * 16 + TILEX * x + 6 + j * 8,
10260                  TILEY * y + 10 + yoffset_ce);
10261
10262       ii /= 10;
10263     }
10264   }
10265
10266   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10267   {
10268     int x = i % 16;
10269     int y = i / 16;
10270     int ii = i + 1;
10271     int j;
10272
10273     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10274                TILEX * x, TILEY * y + yoffset_ge);
10275
10276     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10277                TILEX, TILEY,
10278                TILEX * x + TILEX * 16,
10279                TILEY * y + yoffset_ge);
10280
10281     for (j = 1; j >= 0; j--)
10282     {
10283       int c = ii % 10;
10284
10285       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10286                  TILEX * x + 6 + j * 10,
10287                  TILEY * y + 11 + yoffset_ge);
10288
10289       BlitBitmap(src_bitmap, bitmap,
10290                  TILEX + c * 8, TILEY + 12, 6, 10,
10291                  TILEX * 16 + TILEX * x + 10 + j * 8,
10292                  TILEY * y + 10 + yoffset_ge);
10293
10294       ii /= 10;
10295     }
10296   }
10297
10298   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10299     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10300
10301   FreeBitmap(bitmap);
10302
10303   CloseAllAndExit(0);
10304 #endif
10305 }