fixed bug with marking level as changed for certain non-level-changing gadgets
[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 {
7067   int chunk_size;
7068   int i;
7069   FILE *file;
7070
7071   if (!(file = fopen(filename, MODE_WRITE)))
7072   {
7073     Error(ERR_WARN, "cannot save level file '%s'", filename);
7074     return;
7075   }
7076
7077   level->file_version = FILE_VERSION_ACTUAL;
7078   level->game_version = GAME_VERSION_ACTUAL;
7079
7080   level->creation_date = getCurrentDate();
7081
7082   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7083   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7084
7085   chunk_size = SaveLevel_VERS(NULL, level);
7086   putFileChunkBE(file, "VERS", chunk_size);
7087   SaveLevel_VERS(file, level);
7088
7089   chunk_size = SaveLevel_DATE(NULL, level);
7090   putFileChunkBE(file, "DATE", chunk_size);
7091   SaveLevel_DATE(file, level);
7092
7093   chunk_size = SaveLevel_NAME(NULL, level);
7094   putFileChunkBE(file, "NAME", chunk_size);
7095   SaveLevel_NAME(file, level);
7096
7097   chunk_size = SaveLevel_AUTH(NULL, level);
7098   putFileChunkBE(file, "AUTH", chunk_size);
7099   SaveLevel_AUTH(file, level);
7100
7101   chunk_size = SaveLevel_INFO(NULL, level);
7102   putFileChunkBE(file, "INFO", chunk_size);
7103   SaveLevel_INFO(file, level);
7104
7105   chunk_size = SaveLevel_BODY(NULL, level);
7106   putFileChunkBE(file, "BODY", chunk_size);
7107   SaveLevel_BODY(file, level);
7108
7109   chunk_size = SaveLevel_ELEM(NULL, level);
7110   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          /* save if changed */
7111   {
7112     putFileChunkBE(file, "ELEM", chunk_size);
7113     SaveLevel_ELEM(file, level);
7114   }
7115
7116   for (i = 0; i < NUM_ENVELOPES; i++)
7117   {
7118     int element = EL_ENVELOPE_1 + i;
7119
7120     chunk_size = SaveLevel_NOTE(NULL, level, element);
7121     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        /* save if changed */
7122     {
7123       putFileChunkBE(file, "NOTE", chunk_size);
7124       SaveLevel_NOTE(file, level, element);
7125     }
7126   }
7127
7128   /* if not using template level, check for non-default custom/group elements */
7129   if (!level->use_custom_template)
7130   {
7131     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7132     {
7133       int element = EL_CUSTOM_START + i;
7134
7135       chunk_size = SaveLevel_CUSX(NULL, level, element);
7136       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      /* save if changed */
7137       {
7138         putFileChunkBE(file, "CUSX", chunk_size);
7139         SaveLevel_CUSX(file, level, element);
7140       }
7141     }
7142
7143     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7144     {
7145       int element = EL_GROUP_START + i;
7146
7147       chunk_size = SaveLevel_GRPX(NULL, level, element);
7148       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      /* save if changed */
7149       {
7150         putFileChunkBE(file, "GRPX", chunk_size);
7151         SaveLevel_GRPX(file, level, element);
7152       }
7153     }
7154   }
7155
7156   fclose(file);
7157
7158   SetFilePermissions(filename, PERMS_PRIVATE);
7159 }
7160
7161 void SaveLevel(int nr)
7162 {
7163   char *filename = getDefaultLevelFilename(nr);
7164
7165   SaveLevelFromFilename(&level, filename);
7166 }
7167
7168 void SaveLevelTemplate()
7169 {
7170   char *filename = getLocalLevelTemplateFilename();
7171
7172   SaveLevelFromFilename(&level, filename);
7173 }
7174
7175 boolean SaveLevelChecked(int nr)
7176 {
7177   char *filename = getDefaultLevelFilename(nr);
7178   boolean new_level = !fileExists(filename);
7179   boolean level_saved = FALSE;
7180
7181   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7182   {
7183     SaveLevel(nr);
7184
7185     if (new_level)
7186       Request("Level saved!", REQ_CONFIRM);
7187
7188     level_saved = TRUE;
7189   }
7190
7191   return level_saved;
7192 }
7193
7194 void DumpLevel(struct LevelInfo *level)
7195 {
7196   if (level->no_level_file || level->no_valid_file)
7197   {
7198     Error(ERR_WARN, "cannot dump -- no valid level file found");
7199
7200     return;
7201   }
7202
7203   PrintLine("-", 79);
7204   Print("Level xxx (file version %08d, game version %08d)\n",
7205         level->file_version, level->game_version);
7206   PrintLine("-", 79);
7207
7208   Print("Level author: '%s'\n", level->author);
7209   Print("Level title:  '%s'\n", level->name);
7210   Print("\n");
7211   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7212   Print("\n");
7213   Print("Level time:  %d seconds\n", level->time);
7214   Print("Gems needed: %d\n", level->gems_needed);
7215   Print("\n");
7216   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7217   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7218   Print("Time for light:      %d seconds\n", level->time_light);
7219   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7220   Print("\n");
7221   Print("Amoeba speed: %d\n", level->amoeba_speed);
7222   Print("\n");
7223
7224   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7225   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7226   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7227   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7228   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7229
7230   PrintLine("-", 79);
7231 }
7232
7233
7234 /* ========================================================================= */
7235 /* tape file functions                                                       */
7236 /* ========================================================================= */
7237
7238 static void setTapeInfoToDefaults()
7239 {
7240   int i;
7241
7242   /* always start with reliable default values (empty tape) */
7243   TapeErase();
7244
7245   /* default values (also for pre-1.2 tapes) with only the first player */
7246   tape.player_participates[0] = TRUE;
7247   for (i = 1; i < MAX_PLAYERS; i++)
7248     tape.player_participates[i] = FALSE;
7249
7250   /* at least one (default: the first) player participates in every tape */
7251   tape.num_participating_players = 1;
7252
7253   tape.level_nr = level_nr;
7254   tape.counter = 0;
7255   tape.changed = FALSE;
7256
7257   tape.recording = FALSE;
7258   tape.playing = FALSE;
7259   tape.pausing = FALSE;
7260
7261   tape.no_valid_file = FALSE;
7262 }
7263
7264 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7265 {
7266   tape->file_version = getFileVersion(file);
7267   tape->game_version = getFileVersion(file);
7268
7269   return chunk_size;
7270 }
7271
7272 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7273 {
7274   int i;
7275
7276   tape->random_seed = getFile32BitBE(file);
7277   tape->date        = getFile32BitBE(file);
7278   tape->length      = getFile32BitBE(file);
7279
7280   /* read header fields that are new since version 1.2 */
7281   if (tape->file_version >= FILE_VERSION_1_2)
7282   {
7283     byte store_participating_players = getFile8Bit(file);
7284     int engine_version;
7285
7286     /* since version 1.2, tapes store which players participate in the tape */
7287     tape->num_participating_players = 0;
7288     for (i = 0; i < MAX_PLAYERS; i++)
7289     {
7290       tape->player_participates[i] = FALSE;
7291
7292       if (store_participating_players & (1 << i))
7293       {
7294         tape->player_participates[i] = TRUE;
7295         tape->num_participating_players++;
7296       }
7297     }
7298
7299     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7300
7301     engine_version = getFileVersion(file);
7302     if (engine_version > 0)
7303       tape->engine_version = engine_version;
7304     else
7305       tape->engine_version = tape->game_version;
7306   }
7307
7308   return chunk_size;
7309 }
7310
7311 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7312 {
7313   int level_identifier_size;
7314   int i;
7315
7316   level_identifier_size = getFile16BitBE(file);
7317
7318   tape->level_identifier =
7319     checked_realloc(tape->level_identifier, level_identifier_size);
7320
7321   for (i = 0; i < level_identifier_size; i++)
7322     tape->level_identifier[i] = getFile8Bit(file);
7323
7324   tape->level_nr = getFile16BitBE(file);
7325
7326   chunk_size = 2 + level_identifier_size + 2;
7327
7328   return chunk_size;
7329 }
7330
7331 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7332 {
7333   int i, j;
7334   int chunk_size_expected =
7335     (tape->num_participating_players + 1) * tape->length;
7336
7337   if (chunk_size_expected != chunk_size)
7338   {
7339     ReadUnusedBytesFromFile(file, chunk_size);
7340     return chunk_size_expected;
7341   }
7342
7343   for (i = 0; i < tape->length; i++)
7344   {
7345     if (i >= MAX_TAPE_LEN)
7346       break;
7347
7348     for (j = 0; j < MAX_PLAYERS; j++)
7349     {
7350       tape->pos[i].action[j] = MV_NONE;
7351
7352       if (tape->player_participates[j])
7353         tape->pos[i].action[j] = getFile8Bit(file);
7354     }
7355
7356     tape->pos[i].delay = getFile8Bit(file);
7357
7358     if (tape->file_version == FILE_VERSION_1_0)
7359     {
7360       /* eliminate possible diagonal moves in old tapes */
7361       /* this is only for backward compatibility */
7362
7363       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7364       byte action = tape->pos[i].action[0];
7365       int k, num_moves = 0;
7366
7367       for (k = 0; k<4; k++)
7368       {
7369         if (action & joy_dir[k])
7370         {
7371           tape->pos[i + num_moves].action[0] = joy_dir[k];
7372           if (num_moves > 0)
7373             tape->pos[i + num_moves].delay = 0;
7374           num_moves++;
7375         }
7376       }
7377
7378       if (num_moves > 1)
7379       {
7380         num_moves--;
7381         i += num_moves;
7382         tape->length += num_moves;
7383       }
7384     }
7385     else if (tape->file_version < FILE_VERSION_2_0)
7386     {
7387       /* convert pre-2.0 tapes to new tape format */
7388
7389       if (tape->pos[i].delay > 1)
7390       {
7391         /* action part */
7392         tape->pos[i + 1] = tape->pos[i];
7393         tape->pos[i + 1].delay = 1;
7394
7395         /* delay part */
7396         for (j = 0; j < MAX_PLAYERS; j++)
7397           tape->pos[i].action[j] = MV_NONE;
7398         tape->pos[i].delay--;
7399
7400         i++;
7401         tape->length++;
7402       }
7403     }
7404
7405     if (checkEndOfFile(file))
7406       break;
7407   }
7408
7409   if (i != tape->length)
7410     chunk_size = (tape->num_participating_players + 1) * i;
7411
7412   return chunk_size;
7413 }
7414
7415 void LoadTape_SokobanSolution(char *filename)
7416 {
7417   File *file;
7418   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7419
7420   if (!(file = openFile(filename, MODE_READ)))
7421   {
7422     tape.no_valid_file = TRUE;
7423
7424     return;
7425   }
7426
7427   while (!checkEndOfFile(file))
7428   {
7429     unsigned char c = getByteFromFile(file);
7430
7431     if (checkEndOfFile(file))
7432       break;
7433
7434     switch (c)
7435     {
7436       case 'u':
7437       case 'U':
7438         tape.pos[tape.length].action[0] = MV_UP;
7439         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7440         tape.length++;
7441         break;
7442
7443       case 'd':
7444       case 'D':
7445         tape.pos[tape.length].action[0] = MV_DOWN;
7446         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7447         tape.length++;
7448         break;
7449
7450       case 'l':
7451       case 'L':
7452         tape.pos[tape.length].action[0] = MV_LEFT;
7453         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7454         tape.length++;
7455         break;
7456
7457       case 'r':
7458       case 'R':
7459         tape.pos[tape.length].action[0] = MV_RIGHT;
7460         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7461         tape.length++;
7462         break;
7463
7464       case '\n':
7465       case '\r':
7466       case '\t':
7467       case ' ':
7468         /* ignore white-space characters */
7469         break;
7470
7471       default:
7472         tape.no_valid_file = TRUE;
7473
7474         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7475
7476         break;
7477     }
7478   }
7479
7480   closeFile(file);
7481
7482   if (tape.no_valid_file)
7483     return;
7484
7485   tape.length_frames  = GetTapeLengthFrames();
7486   tape.length_seconds = GetTapeLengthSeconds();
7487 }
7488
7489 void LoadTapeFromFilename(char *filename)
7490 {
7491   char cookie[MAX_LINE_LEN];
7492   char chunk_name[CHUNK_ID_LEN + 1];
7493   File *file;
7494   int chunk_size;
7495
7496   /* always start with reliable default values */
7497   setTapeInfoToDefaults();
7498
7499   if (strSuffix(filename, ".sln"))
7500   {
7501     LoadTape_SokobanSolution(filename);
7502
7503     return;
7504   }
7505
7506   if (!(file = openFile(filename, MODE_READ)))
7507   {
7508     tape.no_valid_file = TRUE;
7509
7510     return;
7511   }
7512
7513   getFileChunkBE(file, chunk_name, NULL);
7514   if (strEqual(chunk_name, "RND1"))
7515   {
7516     getFile32BitBE(file);               /* not used */
7517
7518     getFileChunkBE(file, chunk_name, NULL);
7519     if (!strEqual(chunk_name, "TAPE"))
7520     {
7521       tape.no_valid_file = TRUE;
7522
7523       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7524
7525       closeFile(file);
7526
7527       return;
7528     }
7529   }
7530   else  /* check for pre-2.0 file format with cookie string */
7531   {
7532     strcpy(cookie, chunk_name);
7533     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7534       cookie[4] = '\0';
7535     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7536       cookie[strlen(cookie) - 1] = '\0';
7537
7538     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7539     {
7540       tape.no_valid_file = TRUE;
7541
7542       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7543
7544       closeFile(file);
7545
7546       return;
7547     }
7548
7549     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7550     {
7551       tape.no_valid_file = TRUE;
7552
7553       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7554
7555       closeFile(file);
7556
7557       return;
7558     }
7559
7560     /* pre-2.0 tape files have no game version, so use file version here */
7561     tape.game_version = tape.file_version;
7562   }
7563
7564   if (tape.file_version < FILE_VERSION_1_2)
7565   {
7566     /* tape files from versions before 1.2.0 without chunk structure */
7567     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7568     LoadTape_BODY(file, 2 * tape.length,      &tape);
7569   }
7570   else
7571   {
7572     static struct
7573     {
7574       char *name;
7575       int size;
7576       int (*loader)(File *, int, struct TapeInfo *);
7577     }
7578     chunk_info[] =
7579     {
7580       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
7581       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
7582       { "INFO", -1,                     LoadTape_INFO },
7583       { "BODY", -1,                     LoadTape_BODY },
7584       {  NULL,  0,                      NULL }
7585     };
7586
7587     while (getFileChunkBE(file, chunk_name, &chunk_size))
7588     {
7589       int i = 0;
7590
7591       while (chunk_info[i].name != NULL &&
7592              !strEqual(chunk_name, chunk_info[i].name))
7593         i++;
7594
7595       if (chunk_info[i].name == NULL)
7596       {
7597         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7598               chunk_name, filename);
7599         ReadUnusedBytesFromFile(file, chunk_size);
7600       }
7601       else if (chunk_info[i].size != -1 &&
7602                chunk_info[i].size != chunk_size)
7603       {
7604         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7605               chunk_size, chunk_name, filename);
7606         ReadUnusedBytesFromFile(file, chunk_size);
7607       }
7608       else
7609       {
7610         /* call function to load this tape chunk */
7611         int chunk_size_expected =
7612           (chunk_info[i].loader)(file, chunk_size, &tape);
7613
7614         /* the size of some chunks cannot be checked before reading other
7615            chunks first (like "HEAD" and "BODY") that contain some header
7616            information, so check them here */
7617         if (chunk_size_expected != chunk_size)
7618         {
7619           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7620                 chunk_size, chunk_name, filename);
7621         }
7622       }
7623     }
7624   }
7625
7626   closeFile(file);
7627
7628   tape.length_frames  = GetTapeLengthFrames();
7629   tape.length_seconds = GetTapeLengthSeconds();
7630
7631 #if 0
7632   printf("::: tape file version: %d\n",   tape.file_version);
7633   printf("::: tape game version: %d\n",   tape.game_version);
7634   printf("::: tape engine version: %d\n", tape.engine_version);
7635 #endif
7636 }
7637
7638 void LoadTape(int nr)
7639 {
7640   char *filename = getTapeFilename(nr);
7641
7642   LoadTapeFromFilename(filename);
7643 }
7644
7645 void LoadSolutionTape(int nr)
7646 {
7647   char *filename = getSolutionTapeFilename(nr);
7648
7649   LoadTapeFromFilename(filename);
7650
7651   if (TAPE_IS_EMPTY(tape) &&
7652       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7653       level.native_sp_level->demo.is_available)
7654     CopyNativeTape_SP_to_RND(&level);
7655 }
7656
7657 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7658 {
7659   putFileVersion(file, tape->file_version);
7660   putFileVersion(file, tape->game_version);
7661 }
7662
7663 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7664 {
7665   int i;
7666   byte store_participating_players = 0;
7667
7668   /* set bits for participating players for compact storage */
7669   for (i = 0; i < MAX_PLAYERS; i++)
7670     if (tape->player_participates[i])
7671       store_participating_players |= (1 << i);
7672
7673   putFile32BitBE(file, tape->random_seed);
7674   putFile32BitBE(file, tape->date);
7675   putFile32BitBE(file, tape->length);
7676
7677   putFile8Bit(file, store_participating_players);
7678
7679   /* unused bytes not at the end here for 4-byte alignment of engine_version */
7680   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7681
7682   putFileVersion(file, tape->engine_version);
7683 }
7684
7685 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7686 {
7687   int level_identifier_size = strlen(tape->level_identifier) + 1;
7688   int i;
7689
7690   putFile16BitBE(file, level_identifier_size);
7691
7692   for (i = 0; i < level_identifier_size; i++)
7693     putFile8Bit(file, tape->level_identifier[i]);
7694
7695   putFile16BitBE(file, tape->level_nr);
7696 }
7697
7698 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7699 {
7700   int i, j;
7701
7702   for (i = 0; i < tape->length; i++)
7703   {
7704     for (j = 0; j < MAX_PLAYERS; j++)
7705       if (tape->player_participates[j])
7706         putFile8Bit(file, tape->pos[i].action[j]);
7707
7708     putFile8Bit(file, tape->pos[i].delay);
7709   }
7710 }
7711
7712 void SaveTape(int nr)
7713 {
7714   char *filename = getTapeFilename(nr);
7715   FILE *file;
7716   int num_participating_players = 0;
7717   int info_chunk_size;
7718   int body_chunk_size;
7719   int i;
7720
7721   InitTapeDirectory(leveldir_current->subdir);
7722
7723   if (!(file = fopen(filename, MODE_WRITE)))
7724   {
7725     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7726     return;
7727   }
7728
7729   tape.file_version = FILE_VERSION_ACTUAL;
7730   tape.game_version = GAME_VERSION_ACTUAL;
7731
7732   /* count number of participating players  */
7733   for (i = 0; i < MAX_PLAYERS; i++)
7734     if (tape.player_participates[i])
7735       num_participating_players++;
7736
7737   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7738   body_chunk_size = (num_participating_players + 1) * tape.length;
7739
7740   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7741   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7742
7743   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7744   SaveTape_VERS(file, &tape);
7745
7746   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7747   SaveTape_HEAD(file, &tape);
7748
7749   putFileChunkBE(file, "INFO", info_chunk_size);
7750   SaveTape_INFO(file, &tape);
7751
7752   putFileChunkBE(file, "BODY", body_chunk_size);
7753   SaveTape_BODY(file, &tape);
7754
7755   fclose(file);
7756
7757   SetFilePermissions(filename, PERMS_PRIVATE);
7758
7759   tape.changed = FALSE;
7760 }
7761
7762 boolean SaveTapeChecked(int nr)
7763 {
7764   char *filename = getTapeFilename(nr);
7765   boolean new_tape = !fileExists(filename);
7766   boolean tape_saved = FALSE;
7767
7768   if (new_tape || Request("Replace old tape?", REQ_ASK))
7769   {
7770     SaveTape(nr);
7771
7772     if (new_tape)
7773       Request("Tape saved!", REQ_CONFIRM);
7774
7775     tape_saved = TRUE;
7776   }
7777
7778   return tape_saved;
7779 }
7780
7781 void DumpTape(struct TapeInfo *tape)
7782 {
7783   int tape_frame_counter;
7784   int i, j;
7785
7786   if (tape->no_valid_file)
7787   {
7788     Error(ERR_WARN, "cannot dump -- no valid tape file found");
7789
7790     return;
7791   }
7792
7793   PrintLine("-", 79);
7794   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
7795         tape->level_nr, tape->file_version, tape->game_version);
7796   Print("                  (effective engine version %08d)\n",
7797         tape->engine_version);
7798   Print("Level series identifier: '%s'\n", tape->level_identifier);
7799   PrintLine("-", 79);
7800
7801   tape_frame_counter = 0;
7802
7803   for (i = 0; i < tape->length; i++)
7804   {
7805     if (i >= MAX_TAPE_LEN)
7806       break;
7807
7808     Print("%04d: ", i);
7809
7810     for (j = 0; j < MAX_PLAYERS; j++)
7811     {
7812       if (tape->player_participates[j])
7813       {
7814         int action = tape->pos[i].action[j];
7815
7816         Print("%d:%02x ", j, action);
7817         Print("[%c%c%c%c|%c%c] - ",
7818               (action & JOY_LEFT ? '<' : ' '),
7819               (action & JOY_RIGHT ? '>' : ' '),
7820               (action & JOY_UP ? '^' : ' '),
7821               (action & JOY_DOWN ? 'v' : ' '),
7822               (action & JOY_BUTTON_1 ? '1' : ' '),
7823               (action & JOY_BUTTON_2 ? '2' : ' '));
7824       }
7825     }
7826
7827     Print("(%03d) ", tape->pos[i].delay);
7828     Print("[%05d]\n", tape_frame_counter);
7829
7830     tape_frame_counter += tape->pos[i].delay;
7831   }
7832
7833   PrintLine("-", 79);
7834 }
7835
7836
7837 /* ========================================================================= */
7838 /* score file functions                                                      */
7839 /* ========================================================================= */
7840
7841 void LoadScore(int nr)
7842 {
7843   int i;
7844   char *filename = getScoreFilename(nr);
7845   char cookie[MAX_LINE_LEN];
7846   char line[MAX_LINE_LEN];
7847   char *line_ptr;
7848   FILE *file;
7849
7850   /* always start with reliable default values */
7851   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7852   {
7853     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7854     highscore[i].Score = 0;
7855   }
7856
7857   if (!(file = fopen(filename, MODE_READ)))
7858     return;
7859
7860   /* check file identifier */
7861   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
7862     cookie[0] = '\0';
7863   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7864     cookie[strlen(cookie) - 1] = '\0';
7865
7866   if (!checkCookieString(cookie, SCORE_COOKIE))
7867   {
7868     Error(ERR_WARN, "unknown format of score file '%s'", filename);
7869     fclose(file);
7870     return;
7871   }
7872
7873   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7874   {
7875     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
7876       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
7877     if (fgets(line, MAX_LINE_LEN, file) == NULL)
7878       line[0] = '\0';
7879
7880     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
7881       line[strlen(line) - 1] = '\0';
7882
7883     for (line_ptr = line; *line_ptr; line_ptr++)
7884     {
7885       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7886       {
7887         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7888         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7889         break;
7890       }
7891     }
7892   }
7893
7894   fclose(file);
7895 }
7896
7897 void SaveScore(int nr)
7898 {
7899   int i;
7900   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
7901   char *filename = getScoreFilename(nr);
7902   FILE *file;
7903
7904   InitScoreDirectory(leveldir_current->subdir);
7905
7906   if (!(file = fopen(filename, MODE_WRITE)))
7907   {
7908     Error(ERR_WARN, "cannot save score for level %d", nr);
7909     return;
7910   }
7911
7912   fprintf(file, "%s\n\n", SCORE_COOKIE);
7913
7914   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7915     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7916
7917   fclose(file);
7918
7919   SetFilePermissions(filename, permissions);
7920 }
7921
7922
7923 /* ========================================================================= */
7924 /* setup file functions                                                      */
7925 /* ========================================================================= */
7926
7927 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
7928
7929 /* global setup */
7930 #define SETUP_TOKEN_PLAYER_NAME                 0
7931 #define SETUP_TOKEN_SOUND                       1
7932 #define SETUP_TOKEN_SOUND_LOOPS                 2
7933 #define SETUP_TOKEN_SOUND_MUSIC                 3
7934 #define SETUP_TOKEN_SOUND_SIMPLE                4
7935 #define SETUP_TOKEN_TOONS                       5
7936 #define SETUP_TOKEN_SCROLL_DELAY                6
7937 #define SETUP_TOKEN_SCROLL_DELAY_VALUE          7
7938 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE        8
7939 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY      9
7940 #define SETUP_TOKEN_FADE_SCREENS                10
7941 #define SETUP_TOKEN_AUTORECORD                  11
7942 #define SETUP_TOKEN_SHOW_TITLESCREEN            12
7943 #define SETUP_TOKEN_QUICK_DOORS                 13
7944 #define SETUP_TOKEN_TEAM_MODE                   14
7945 #define SETUP_TOKEN_HANDICAP                    15
7946 #define SETUP_TOKEN_SKIP_LEVELS                 16
7947 #define SETUP_TOKEN_TIME_LIMIT                  17
7948 #define SETUP_TOKEN_FULLSCREEN                  18
7949 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT      19
7950 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY      20
7951 #define SETUP_TOKEN_SCREEN_RENDERING_MODE       21
7952 #define SETUP_TOKEN_ASK_ON_ESCAPE               22
7953 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        23
7954 #define SETUP_TOKEN_QUICK_SWITCH                24
7955 #define SETUP_TOKEN_INPUT_ON_FOCUS              25
7956 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         26
7957 #define SETUP_TOKEN_GAME_FRAME_DELAY            27
7958 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS     28
7959 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS         29
7960 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS       30
7961 #define SETUP_TOKEN_GRAPHICS_SET                31
7962 #define SETUP_TOKEN_SOUNDS_SET                  32
7963 #define SETUP_TOKEN_MUSIC_SET                   33
7964 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     34
7965 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       35
7966 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        36
7967 #define SETUP_TOKEN_VOLUME_SIMPLE               37
7968 #define SETUP_TOKEN_VOLUME_LOOPS                38
7969 #define SETUP_TOKEN_VOLUME_MUSIC                39
7970 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE          40
7971 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE         41
7972 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE         42
7973
7974 #define NUM_GLOBAL_SETUP_TOKENS                 43
7975
7976 /* editor setup */
7977 #define SETUP_TOKEN_EDITOR_EL_CHARS             0
7978 #define SETUP_TOKEN_EDITOR_EL_STEEL_CHARS       1
7979 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            2
7980 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      3
7981 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           4
7982 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   5
7983
7984 #define NUM_EDITOR_SETUP_TOKENS                 6
7985
7986 /* editor cascade setup */
7987 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
7988 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
7989 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
7990 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
7991 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
7992 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
7993 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
7994 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
7995 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
7996 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT    9
7997 #define SETUP_TOKEN_EDITOR_CASCADE_CE           10
7998 #define SETUP_TOKEN_EDITOR_CASCADE_GE           11
7999 #define SETUP_TOKEN_EDITOR_CASCADE_REF          12
8000 #define SETUP_TOKEN_EDITOR_CASCADE_USER         13
8001 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      14
8002
8003 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         15
8004
8005 /* shortcut setup */
8006 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
8007 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
8008 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
8009 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
8010 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
8011 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
8012 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
8013 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
8014 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT         8
8015 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA         9
8016 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP          10
8017 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE         11
8018 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD        12
8019 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY          13
8020 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE       14
8021 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS        15
8022 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC        16
8023 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT          17
8024 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT         18
8025 #define SETUP_TOKEN_SHORTCUT_SNAP_UP            19
8026 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN          20
8027
8028 #define NUM_SHORTCUT_SETUP_TOKENS               21
8029
8030 /* player setup */
8031 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
8032 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
8033 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
8034 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
8035 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
8036 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
8037 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
8038 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
8039 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
8040 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
8041 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
8042 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
8043 #define SETUP_TOKEN_PLAYER_KEY_UP               12
8044 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
8045 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
8046 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
8047
8048 #define NUM_PLAYER_SETUP_TOKENS                 16
8049
8050 /* system setup */
8051 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER      0
8052 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      1
8053 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  2
8054
8055 #define NUM_SYSTEM_SETUP_TOKENS                 3
8056
8057 /* internal setup */
8058 #define SETUP_TOKEN_INT_PROGRAM_TITLE           0
8059 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR          1
8060 #define SETUP_TOKEN_INT_PROGRAM_EMAIL           2
8061 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE         3
8062 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT       4
8063 #define SETUP_TOKEN_INT_PROGRAM_COMPANY         5
8064 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE       6
8065 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET    7
8066 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET      8
8067 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET       9
8068 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE  10
8069 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE    11
8070 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE     12
8071 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES    13
8072 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14
8073 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH    15
8074 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT   16
8075
8076 #define NUM_INTERNAL_SETUP_TOKENS               17
8077
8078 /* debug setup */
8079 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0         0
8080 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1         1
8081 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2         2
8082 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3         3
8083 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4         4
8084 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5         5
8085 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6         6
8086 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7         7
8087 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8         8
8088 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9         9
8089 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0     10
8090 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1     11
8091 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2     12
8092 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3     13
8093 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4     14
8094 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5     15
8095 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6     16
8096 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7     17
8097 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8     18
8098 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9     19
8099 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8100 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8101
8102 #define NUM_DEBUG_SETUP_TOKENS                  22
8103
8104 /* options setup */
8105 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
8106
8107 #define NUM_OPTIONS_SETUP_TOKENS                1
8108
8109
8110 static struct SetupInfo si;
8111 static struct SetupEditorInfo sei;
8112 static struct SetupEditorCascadeInfo seci;
8113 static struct SetupShortcutInfo ssi;
8114 static struct SetupInputInfo sii;
8115 static struct SetupSystemInfo syi;
8116 static struct SetupInternalInfo sxi;
8117 static struct SetupDebugInfo sdi;
8118 static struct OptionInfo soi;
8119
8120 static struct TokenInfo global_setup_tokens[] =
8121 {
8122   { TYPE_STRING, &si.player_name,             "player_name"             },
8123   { TYPE_SWITCH, &si.sound,                   "sound"                   },
8124   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
8125   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
8126   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
8127   { TYPE_SWITCH, &si.toons,                   "toons"                   },
8128   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
8129   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
8130   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"    },
8131   { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory"  },
8132   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
8133   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
8134   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
8135   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
8136   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
8137   { TYPE_SWITCH, &si.handicap,                "handicap"                },
8138   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
8139   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
8140   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
8141   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
8142   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
8143   { TYPE_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"   },
8144   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
8145   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
8146   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
8147   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
8148   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
8149   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
8150   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8151   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
8152   { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"   },
8153   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
8154   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
8155   { TYPE_STRING, &si.music_set,               "music_set"               },
8156   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8157   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
8158   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
8159   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
8160   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
8161   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
8162   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
8163   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
8164   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
8165 };
8166
8167 static struct TokenInfo editor_setup_tokens[] =
8168 {
8169   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
8170   { TYPE_SWITCH, &sei.el_steel_chars,   "editor.el_steel_chars"         },
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.show_element_token,"editor.show_element_token"    },
8175 };
8176
8177 static struct TokenInfo editor_cascade_setup_tokens[] =
8178 {
8179   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
8180   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
8181   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
8182   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
8183   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
8184   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
8185   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
8186   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
8187   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
8188   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
8189   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
8190   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
8191   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
8192   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
8193   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
8194 };
8195
8196 static struct TokenInfo shortcut_setup_tokens[] =
8197 {
8198   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
8199   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
8200   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
8201   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
8202   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
8203   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
8204   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
8205   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
8206   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
8207   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
8208   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
8209   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
8210   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
8211   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
8212   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
8213   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
8214   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
8215   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
8216   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
8217   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
8218   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
8219 };
8220
8221 static struct TokenInfo player_setup_tokens[] =
8222 {
8223   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
8224   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
8225   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
8226   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
8227   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
8228   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
8229   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
8230   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
8231   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
8232   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
8233   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
8234   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
8235   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
8236   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
8237   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
8238   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
8239 };
8240
8241 static struct TokenInfo system_setup_tokens[] =
8242 {
8243   { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"     },
8244   { TYPE_STRING,  &syi.sdl_audiodriver,    "system.sdl_audiodriver"     },
8245   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8246 };
8247
8248 static struct TokenInfo internal_setup_tokens[] =
8249 {
8250   { TYPE_STRING, &sxi.program_title,            "program_title"         },
8251   { TYPE_STRING, &sxi.program_author,           "program_author"        },
8252   { TYPE_STRING, &sxi.program_email,            "program_email"         },
8253   { TYPE_STRING, &sxi.program_website,          "program_website"       },
8254   { TYPE_STRING, &sxi.program_copyright,        "program_copyright"     },
8255   { TYPE_STRING, &sxi.program_company,          "program_company"       },
8256   { TYPE_STRING, &sxi.program_icon_file,        "program_icon_file"     },
8257   { TYPE_STRING, &sxi.default_graphics_set,     "default_graphics_set"  },
8258   { TYPE_STRING, &sxi.default_sounds_set,       "default_sounds_set"    },
8259   { TYPE_STRING, &sxi.default_music_set,        "default_music_set"     },
8260   { TYPE_STRING, &sxi.fallback_graphics_file,   "fallback_graphics_file"},
8261   { TYPE_STRING, &sxi.fallback_sounds_file,     "fallback_sounds_file"  },
8262   { TYPE_STRING, &sxi.fallback_music_file,      "fallback_music_file"   },
8263   { TYPE_STRING, &sxi.default_level_series,     "default_level_series"  },
8264   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8265   { TYPE_INTEGER,&sxi.default_window_width,     "default_window_width"  },
8266   { TYPE_INTEGER,&sxi.default_window_height,    "default_window_height" },
8267 };
8268
8269 static struct TokenInfo debug_setup_tokens[] =
8270 {
8271   { TYPE_INTEGER, &sdi.frame_delay[0],          "debug.frame_delay_0"   },
8272   { TYPE_INTEGER, &sdi.frame_delay[1],          "debug.frame_delay_1"   },
8273   { TYPE_INTEGER, &sdi.frame_delay[2],          "debug.frame_delay_2"   },
8274   { TYPE_INTEGER, &sdi.frame_delay[3],          "debug.frame_delay_3"   },
8275   { TYPE_INTEGER, &sdi.frame_delay[4],          "debug.frame_delay_4"   },
8276   { TYPE_INTEGER, &sdi.frame_delay[5],          "debug.frame_delay_5"   },
8277   { TYPE_INTEGER, &sdi.frame_delay[6],          "debug.frame_delay_6"   },
8278   { TYPE_INTEGER, &sdi.frame_delay[7],          "debug.frame_delay_7"   },
8279   { TYPE_INTEGER, &sdi.frame_delay[8],          "debug.frame_delay_8"   },
8280   { TYPE_INTEGER, &sdi.frame_delay[9],          "debug.frame_delay_9"   },
8281   { TYPE_KEY_X11, &sdi.frame_delay_key[0],      "debug.key.frame_delay_0" },
8282   { TYPE_KEY_X11, &sdi.frame_delay_key[1],      "debug.key.frame_delay_1" },
8283   { TYPE_KEY_X11, &sdi.frame_delay_key[2],      "debug.key.frame_delay_2" },
8284   { TYPE_KEY_X11, &sdi.frame_delay_key[3],      "debug.key.frame_delay_3" },
8285   { TYPE_KEY_X11, &sdi.frame_delay_key[4],      "debug.key.frame_delay_4" },
8286   { TYPE_KEY_X11, &sdi.frame_delay_key[5],      "debug.key.frame_delay_5" },
8287   { TYPE_KEY_X11, &sdi.frame_delay_key[6],      "debug.key.frame_delay_6" },
8288   { TYPE_KEY_X11, &sdi.frame_delay_key[7],      "debug.key.frame_delay_7" },
8289   { TYPE_KEY_X11, &sdi.frame_delay_key[8],      "debug.key.frame_delay_8" },
8290   { TYPE_KEY_X11, &sdi.frame_delay_key[9],      "debug.key.frame_delay_9" },
8291   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8292   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
8293 };
8294
8295 static struct TokenInfo options_setup_tokens[] =
8296 {
8297   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
8298 };
8299
8300 static char *get_corrected_login_name(char *login_name)
8301 {
8302   /* needed because player name must be a fixed length string */
8303   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8304
8305   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8306   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8307
8308   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
8309     if (strchr(login_name_new, ' '))
8310       *strchr(login_name_new, ' ') = '\0';
8311
8312   return login_name_new;
8313 }
8314
8315 static void setSetupInfoToDefaults(struct SetupInfo *si)
8316 {
8317   int i;
8318
8319   si->player_name = get_corrected_login_name(getLoginName());
8320
8321   si->sound = TRUE;
8322   si->sound_loops = TRUE;
8323   si->sound_music = TRUE;
8324   si->sound_simple = TRUE;
8325   si->toons = TRUE;
8326   si->scroll_delay = TRUE;
8327   si->scroll_delay_value = STD_SCROLL_DELAY;
8328   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8329   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8330   si->fade_screens = TRUE;
8331   si->autorecord = TRUE;
8332   si->show_titlescreen = TRUE;
8333   si->quick_doors = FALSE;
8334   si->team_mode = FALSE;
8335   si->handicap = TRUE;
8336   si->skip_levels = TRUE;
8337   si->time_limit = TRUE;
8338   si->fullscreen = FALSE;
8339   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8340   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8341   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8342   si->ask_on_escape = TRUE;
8343   si->ask_on_escape_editor = TRUE;
8344   si->quick_switch = FALSE;
8345   si->input_on_focus = FALSE;
8346   si->prefer_aga_graphics = TRUE;
8347   si->game_frame_delay = GAME_FRAME_DELAY;
8348   si->sp_show_border_elements = FALSE;
8349   si->small_game_graphics = FALSE;
8350   si->show_snapshot_buttons = FALSE;
8351
8352   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8353   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8354   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8355
8356   si->override_level_graphics = FALSE;
8357   si->override_level_sounds = FALSE;
8358   si->override_level_music = FALSE;
8359
8360   si->volume_simple = 100;              /* percent */
8361   si->volume_loops = 100;               /* percent */
8362   si->volume_music = 100;               /* percent */
8363
8364   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8365   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
8366   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
8367
8368   si->editor.el_boulderdash             = TRUE;
8369   si->editor.el_emerald_mine            = TRUE;
8370   si->editor.el_emerald_mine_club       = TRUE;
8371   si->editor.el_more                    = TRUE;
8372   si->editor.el_sokoban                 = TRUE;
8373   si->editor.el_supaplex                = TRUE;
8374   si->editor.el_diamond_caves           = TRUE;
8375   si->editor.el_dx_boulderdash          = TRUE;
8376   si->editor.el_chars                   = TRUE;
8377   si->editor.el_steel_chars             = TRUE;
8378   si->editor.el_custom                  = TRUE;
8379
8380   si->editor.el_headlines = TRUE;
8381   si->editor.el_user_defined = FALSE;
8382   si->editor.el_dynamic = TRUE;
8383
8384   si->editor.show_element_token = FALSE;
8385
8386   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
8387   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
8388   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
8389
8390   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
8391   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
8392   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
8393   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
8394   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8395
8396   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
8397   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
8398   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
8399   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
8400   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
8401   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
8402
8403   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
8404   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
8405   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
8406
8407   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
8408   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
8409   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
8410   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
8411
8412   for (i = 0; i < MAX_PLAYERS; i++)
8413   {
8414     si->input[i].use_joystick = FALSE;
8415     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8416     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
8417     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8418     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
8419     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
8420     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8421     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
8422     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
8423     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
8424     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
8425     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8426     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
8427     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
8428     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
8429     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
8430   }
8431
8432   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8433   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8434   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8435
8436   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
8437   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
8438   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
8439   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
8440   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8441   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
8442
8443   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8444
8445   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8446   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8447   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8448
8449   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8450   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
8451   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
8452
8453   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8454   si->internal.choose_from_top_leveldir = FALSE;
8455
8456   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
8457   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8458
8459   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8460   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8461   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8462   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8463   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8464   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8465   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8466   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8467   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8468   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8469
8470   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8471   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8472   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8473   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8474   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8475   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8476   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8477   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8478   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8479   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8480
8481   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8482   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
8483
8484   si->options.verbose = FALSE;
8485
8486 #if defined(PLATFORM_ANDROID)
8487   si->fullscreen = TRUE;
8488 #endif
8489 }
8490
8491 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8492 {
8493   si->editor_cascade.el_bd              = TRUE;
8494   si->editor_cascade.el_em              = TRUE;
8495   si->editor_cascade.el_emc             = TRUE;
8496   si->editor_cascade.el_rnd             = TRUE;
8497   si->editor_cascade.el_sb              = TRUE;
8498   si->editor_cascade.el_sp              = TRUE;
8499   si->editor_cascade.el_dc              = TRUE;
8500   si->editor_cascade.el_dx              = TRUE;
8501
8502   si->editor_cascade.el_chars           = FALSE;
8503   si->editor_cascade.el_steel_chars     = FALSE;
8504   si->editor_cascade.el_ce              = FALSE;
8505   si->editor_cascade.el_ge              = FALSE;
8506   si->editor_cascade.el_ref             = FALSE;
8507   si->editor_cascade.el_user            = FALSE;
8508   si->editor_cascade.el_dynamic         = FALSE;
8509 }
8510
8511 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8512 {
8513   int i, pnr;
8514
8515   if (!setup_file_hash)
8516     return;
8517
8518   /* global setup */
8519   si = setup;
8520   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8521     setSetupInfo(global_setup_tokens, i,
8522                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
8523   setup = si;
8524
8525   /* editor setup */
8526   sei = setup.editor;
8527   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8528     setSetupInfo(editor_setup_tokens, i,
8529                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
8530   setup.editor = sei;
8531
8532   /* shortcut setup */
8533   ssi = setup.shortcut;
8534   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8535     setSetupInfo(shortcut_setup_tokens, i,
8536                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
8537   setup.shortcut = ssi;
8538
8539   /* player setup */
8540   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8541   {
8542     char prefix[30];
8543
8544     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8545
8546     sii = setup.input[pnr];
8547     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8548     {
8549       char full_token[100];
8550
8551       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8552       setSetupInfo(player_setup_tokens, i,
8553                    getHashEntry(setup_file_hash, full_token));
8554     }
8555     setup.input[pnr] = sii;
8556   }
8557
8558   /* system setup */
8559   syi = setup.system;
8560   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8561     setSetupInfo(system_setup_tokens, i,
8562                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
8563   setup.system = syi;
8564
8565   /* internal setup */
8566   sxi = setup.internal;
8567   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8568     setSetupInfo(internal_setup_tokens, i,
8569                  getHashEntry(setup_file_hash, internal_setup_tokens[i].text));
8570   setup.internal = sxi;
8571
8572   /* debug setup */
8573   sdi = setup.debug;
8574   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8575     setSetupInfo(debug_setup_tokens, i,
8576                  getHashEntry(setup_file_hash, debug_setup_tokens[i].text));
8577   setup.debug = sdi;
8578
8579   /* options setup */
8580   soi = setup.options;
8581   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8582     setSetupInfo(options_setup_tokens, i,
8583                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
8584   setup.options = soi;
8585 }
8586
8587 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8588 {
8589   int i;
8590
8591   if (!setup_file_hash)
8592     return;
8593
8594   /* editor cascade setup */
8595   seci = setup.editor_cascade;
8596   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8597     setSetupInfo(editor_cascade_setup_tokens, i,
8598                  getHashEntry(setup_file_hash,
8599                               editor_cascade_setup_tokens[i].text));
8600   setup.editor_cascade = seci;
8601 }
8602
8603 void LoadSetupFromFilename(char *filename)
8604 {
8605   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8606
8607   if (setup_file_hash)
8608   {
8609     decodeSetupFileHash(setup_file_hash);
8610
8611     freeSetupFileHash(setup_file_hash);
8612   }
8613   else
8614   {
8615     Error(ERR_WARN, "using default setup values");
8616   }
8617 }
8618
8619 static void LoadSetup_SpecialPostProcessing()
8620 {
8621   char *player_name_new;
8622
8623   /* needed to work around problems with fixed length strings */
8624   player_name_new = get_corrected_login_name(setup.player_name);
8625   free(setup.player_name);
8626   setup.player_name = player_name_new;
8627
8628   /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8629   if (setup.scroll_delay == FALSE)
8630   {
8631     setup.scroll_delay_value = MIN_SCROLL_DELAY;
8632     setup.scroll_delay = TRUE;                  /* now always "on" */
8633   }
8634
8635   /* make sure that scroll delay value stays inside valid range */
8636   setup.scroll_delay_value =
8637     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8638 }
8639
8640 void LoadSetup()
8641 {
8642   char *filename;
8643
8644   /* always start with reliable default values */
8645   setSetupInfoToDefaults(&setup);
8646
8647   /* try to load setup values from default setup file */
8648   filename = getDefaultSetupFilename();
8649
8650   if (fileExists(filename))
8651     LoadSetupFromFilename(filename);
8652
8653   /* try to load setup values from user setup file */
8654   filename = getSetupFilename();
8655
8656   LoadSetupFromFilename(filename);
8657
8658   LoadSetup_SpecialPostProcessing();
8659 }
8660
8661 void LoadSetup_EditorCascade()
8662 {
8663   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8664   SetupFileHash *setup_file_hash = NULL;
8665
8666   /* always start with reliable default values */
8667   setSetupInfoToDefaults_EditorCascade(&setup);
8668
8669   setup_file_hash = loadSetupFileHash(filename);
8670
8671   if (setup_file_hash)
8672   {
8673     decodeSetupFileHash_EditorCascade(setup_file_hash);
8674
8675     freeSetupFileHash(setup_file_hash);
8676   }
8677
8678   free(filename);
8679 }
8680
8681 void SaveSetup()
8682 {
8683   char *filename = getSetupFilename();
8684   FILE *file;
8685   int i, pnr;
8686
8687   InitUserDataDirectory();
8688
8689   if (!(file = fopen(filename, MODE_WRITE)))
8690   {
8691     Error(ERR_WARN, "cannot write setup file '%s'", filename);
8692     return;
8693   }
8694
8695   fprintFileHeader(file, SETUP_FILENAME);
8696
8697   /* global setup */
8698   si = setup;
8699   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8700   {
8701     /* just to make things nicer :) */
8702     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8703         i == SETUP_TOKEN_GRAPHICS_SET ||
8704         i == SETUP_TOKEN_VOLUME_SIMPLE ||
8705         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8706       fprintf(file, "\n");
8707
8708     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8709   }
8710
8711   /* editor setup */
8712   sei = setup.editor;
8713   fprintf(file, "\n");
8714   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8715     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8716
8717   /* shortcut setup */
8718   ssi = setup.shortcut;
8719   fprintf(file, "\n");
8720   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8721     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8722
8723   /* player setup */
8724   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8725   {
8726     char prefix[30];
8727
8728     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8729     fprintf(file, "\n");
8730
8731     sii = setup.input[pnr];
8732     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8733       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8734   }
8735
8736   /* system setup */
8737   syi = setup.system;
8738   fprintf(file, "\n");
8739   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8740     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8741
8742   /* internal setup */
8743   /* (internal setup values not saved to user setup file) */
8744
8745   /* debug setup */
8746   sdi = setup.debug;
8747   fprintf(file, "\n");
8748   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8749     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
8750
8751   /* options setup */
8752   soi = setup.options;
8753   fprintf(file, "\n");
8754   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8755     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8756
8757   fclose(file);
8758
8759   SetFilePermissions(filename, PERMS_PRIVATE);
8760 }
8761
8762 void SaveSetup_EditorCascade()
8763 {
8764   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8765   FILE *file;
8766   int i;
8767
8768   InitUserDataDirectory();
8769
8770   if (!(file = fopen(filename, MODE_WRITE)))
8771   {
8772     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8773     free(filename);
8774     return;
8775   }
8776
8777   fprintFileHeader(file, EDITORCASCADE_FILENAME);
8778
8779   seci = setup.editor_cascade;
8780   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8781     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8782
8783   fclose(file);
8784
8785   SetFilePermissions(filename, PERMS_PRIVATE);
8786
8787   free(filename);
8788 }
8789
8790 void LoadCustomElementDescriptions()
8791 {
8792   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8793   SetupFileHash *setup_file_hash;
8794   int i;
8795
8796   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8797   {
8798     if (element_info[i].custom_description != NULL)
8799     {
8800       free(element_info[i].custom_description);
8801       element_info[i].custom_description = NULL;
8802     }
8803   }
8804
8805   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
8806     return;
8807
8808   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
8809   {
8810     char *token = getStringCat2(element_info[i].token_name, ".name");
8811     char *value = getHashEntry(setup_file_hash, token);
8812
8813     if (value != NULL)
8814       element_info[i].custom_description = getStringCopy(value);
8815
8816     free(token);
8817   }
8818
8819   freeSetupFileHash(setup_file_hash);
8820 }
8821
8822 static int getElementFromToken(char *token)
8823 {
8824   char *value = getHashEntry(element_token_hash, token);
8825
8826   if (value != NULL)
8827     return atoi(value);
8828
8829   Error(ERR_WARN, "unknown element token '%s'", token);
8830
8831   return EL_UNDEFINED;
8832 }
8833
8834 static int get_token_parameter_value(char *token, char *value_raw)
8835 {
8836   char *suffix;
8837
8838   if (token == NULL || value_raw == NULL)
8839     return ARG_UNDEFINED_VALUE;
8840
8841   suffix = strrchr(token, '.');
8842   if (suffix == NULL)
8843     suffix = token;
8844
8845   if (strEqual(suffix, ".element"))
8846     return getElementFromToken(value_raw);
8847
8848   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
8849   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
8850 }
8851
8852 void InitMenuDesignSettings_Static()
8853 {
8854   int i;
8855
8856   /* always start with reliable default values from static default config */
8857   for (i = 0; image_config_vars[i].token != NULL; i++)
8858   {
8859     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
8860
8861     if (value != NULL)
8862       *image_config_vars[i].value =
8863         get_token_parameter_value(image_config_vars[i].token, value);
8864   }
8865 }
8866
8867 static void InitMenuDesignSettings_SpecialPreProcessing()
8868 {
8869   int i;
8870
8871   /* the following initializes hierarchical values from static configuration */
8872
8873   /* special case: initialize "ARG_DEFAULT" values in static default config */
8874   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
8875   titlescreen_initial_first_default.fade_mode  =
8876     title_initial_first_default.fade_mode;
8877   titlescreen_initial_first_default.fade_delay =
8878     title_initial_first_default.fade_delay;
8879   titlescreen_initial_first_default.post_delay =
8880     title_initial_first_default.post_delay;
8881   titlescreen_initial_first_default.auto_delay =
8882     title_initial_first_default.auto_delay;
8883   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
8884   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
8885   titlescreen_first_default.post_delay = title_first_default.post_delay;
8886   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
8887   titlemessage_initial_first_default.fade_mode  =
8888     title_initial_first_default.fade_mode;
8889   titlemessage_initial_first_default.fade_delay =
8890     title_initial_first_default.fade_delay;
8891   titlemessage_initial_first_default.post_delay =
8892     title_initial_first_default.post_delay;
8893   titlemessage_initial_first_default.auto_delay =
8894     title_initial_first_default.auto_delay;
8895   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
8896   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
8897   titlemessage_first_default.post_delay = title_first_default.post_delay;
8898   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
8899
8900   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
8901   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
8902   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
8903   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
8904   titlescreen_default.fade_mode  = title_default.fade_mode;
8905   titlescreen_default.fade_delay = title_default.fade_delay;
8906   titlescreen_default.post_delay = title_default.post_delay;
8907   titlescreen_default.auto_delay = title_default.auto_delay;
8908   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
8909   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
8910   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
8911   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
8912   titlemessage_default.fade_mode  = title_default.fade_mode;
8913   titlemessage_default.fade_delay = title_default.fade_delay;
8914   titlemessage_default.post_delay = title_default.post_delay;
8915   titlemessage_default.auto_delay = title_default.auto_delay;
8916
8917   /* special case: initialize "ARG_DEFAULT" values in static default config */
8918   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
8919   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
8920   {
8921     titlescreen_initial_first[i] = titlescreen_initial_first_default;
8922     titlescreen_first[i] = titlescreen_first_default;
8923     titlemessage_initial_first[i] = titlemessage_initial_first_default;
8924     titlemessage_first[i] = titlemessage_first_default;
8925
8926     titlescreen_initial[i] = titlescreen_initial_default;
8927     titlescreen[i] = titlescreen_default;
8928     titlemessage_initial[i] = titlemessage_initial_default;
8929     titlemessage[i] = titlemessage_default;
8930   }
8931
8932   /* special case: initialize "ARG_DEFAULT" values in static default config */
8933   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
8934   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8935   {
8936     if (i == GFX_SPECIAL_ARG_TITLE)     /* title values already initialized */
8937       continue;
8938
8939     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
8940     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
8941     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
8942   }
8943
8944   /* special case: initialize "ARG_DEFAULT" values in static default config */
8945   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
8946   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
8947   {
8948     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
8949     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
8950     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
8951
8952     if (i == GFX_SPECIAL_ARG_EDITOR)    /* editor values already initialized */
8953       continue;
8954
8955     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
8956   }
8957 }
8958
8959 static void InitMenuDesignSettings_SpecialPostProcessing()
8960 {
8961   static struct
8962   {
8963     struct XY *dst, *src;
8964   }
8965   game_buttons_xy[] =
8966   {
8967     { &game.button.save,        &game.button.stop       },
8968     { &game.button.pause2,      &game.button.pause      },
8969     { &game.button.load,        &game.button.play       },
8970     { &game.button.undo,        &game.button.stop       },
8971     { &game.button.redo,        &game.button.play       },
8972
8973     { NULL,                     NULL                    }
8974   };
8975   int i;
8976
8977   /* special case: initialize later added SETUP list size from LEVELS value */
8978   if (menu.list_size[GAME_MODE_SETUP] == -1)
8979     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
8980
8981   /* set default position for snapshot buttons to stop/pause/play buttons */
8982   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
8983     if ((*game_buttons_xy[i].dst).x == -1 &&
8984         (*game_buttons_xy[i].dst).y == -1)
8985       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
8986 }
8987
8988 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
8989 {
8990   static struct
8991   {
8992     struct XYTileSize *dst, *src;
8993     int graphic;
8994   }
8995   editor_buttons_xy[] =
8996   {
8997     {
8998       &editor.button.element_left,      &editor.palette.element_left,
8999       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9000     },
9001     {
9002       &editor.button.element_middle,    &editor.palette.element_middle,
9003       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9004     },
9005     {
9006       &editor.button.element_right,     &editor.palette.element_right,
9007       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9008     },
9009
9010     { NULL,                     NULL                    }
9011   };
9012   int i;
9013
9014   /* set default position for element buttons to element graphics */
9015   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9016   {
9017     if ((*editor_buttons_xy[i].dst).x == -1 &&
9018         (*editor_buttons_xy[i].dst).y == -1)
9019     {
9020       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9021
9022       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9023
9024       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9025     }
9026   }
9027 }
9028
9029 static void LoadMenuDesignSettingsFromFilename(char *filename)
9030 {
9031   static struct TitleFadingInfo tfi;
9032   static struct TitleMessageInfo tmi;
9033   static struct TokenInfo title_tokens[] =
9034   {
9035     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
9036     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
9037     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
9038     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
9039
9040     { -1,               NULL,                   NULL                    }
9041   };
9042   static struct TokenInfo titlemessage_tokens[] =
9043   {
9044     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9045     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9046     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9047     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9048     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9049     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9050     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9051     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9052     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9053     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9054     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9055     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9056     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9057     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9058     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9059     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9060     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9061
9062     { -1,               NULL,                   NULL                    }
9063   };
9064   static struct
9065   {
9066     struct TitleFadingInfo *info;
9067     char *text;
9068   }
9069   title_info[] =
9070   {
9071     /* initialize first titles from "enter screen" definitions, if defined */
9072     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
9073     { &title_first_default,             "menu.enter_screen.TITLE"       },
9074
9075     /* initialize title screens from "next screen" definitions, if defined */
9076     { &title_initial_default,           "menu.next_screen.TITLE"        },
9077     { &title_default,                   "menu.next_screen.TITLE"        },
9078
9079     { NULL,                             NULL                            }
9080   };
9081   static struct
9082   {
9083     struct TitleMessageInfo *array;
9084     char *text;
9085   }
9086   titlemessage_arrays[] =
9087   {
9088     /* initialize first titles from "enter screen" definitions, if defined */
9089     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
9090     { titlescreen_first,                "menu.enter_screen.TITLE"       },
9091     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
9092     { titlemessage_first,               "menu.enter_screen.TITLE"       },
9093
9094     /* initialize titles from "next screen" definitions, if defined */
9095     { titlescreen_initial,              "menu.next_screen.TITLE"        },
9096     { titlescreen,                      "menu.next_screen.TITLE"        },
9097     { titlemessage_initial,             "menu.next_screen.TITLE"        },
9098     { titlemessage,                     "menu.next_screen.TITLE"        },
9099
9100     /* overwrite titles with title definitions, if defined */
9101     { titlescreen_initial_first,        "[title_initial]"               },
9102     { titlescreen_first,                "[title]"                       },
9103     { titlemessage_initial_first,       "[title_initial]"               },
9104     { titlemessage_first,               "[title]"                       },
9105
9106     { titlescreen_initial,              "[title_initial]"               },
9107     { titlescreen,                      "[title]"                       },
9108     { titlemessage_initial,             "[title_initial]"               },
9109     { titlemessage,                     "[title]"                       },
9110
9111     /* overwrite titles with title screen/message definitions, if defined */
9112     { titlescreen_initial_first,        "[titlescreen_initial]"         },
9113     { titlescreen_first,                "[titlescreen]"                 },
9114     { titlemessage_initial_first,       "[titlemessage_initial]"        },
9115     { titlemessage_first,               "[titlemessage]"                },
9116
9117     { titlescreen_initial,              "[titlescreen_initial]"         },
9118     { titlescreen,                      "[titlescreen]"                 },
9119     { titlemessage_initial,             "[titlemessage_initial]"        },
9120     { titlemessage,                     "[titlemessage]"                },
9121
9122     { NULL,                             NULL                            }
9123   };
9124   SetupFileHash *setup_file_hash;
9125   int i, j, k;
9126
9127   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9128     return;
9129
9130   /* the following initializes hierarchical values from dynamic configuration */
9131
9132   /* special case: initialize with default values that may be overwritten */
9133   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9134   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9135   {
9136     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9137     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9138     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9139
9140     if (value_1 != NULL)
9141       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9142     if (value_2 != NULL)
9143       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9144     if (value_3 != NULL)
9145       menu.list_size[i] = get_integer_from_string(value_3);
9146   }
9147
9148   /* special case: initialize with default values that may be overwritten */
9149   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9150   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9151   {
9152     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9153     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9154
9155     if (value_1 != NULL)
9156       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9157     if (value_2 != NULL)
9158       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9159
9160     if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9161     {
9162       char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9163
9164       if (value_1 != NULL)
9165         menu.list_size_info[i] = get_integer_from_string(value_1);
9166     }
9167   }
9168
9169   /* special case: initialize with default values that may be overwritten */
9170   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9171   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9172   {
9173     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9174     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9175
9176     if (value_1 != NULL)
9177       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9178     if (value_2 != NULL)
9179       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9180   }
9181
9182   /* special case: initialize with default values that may be overwritten */
9183   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9184   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9185   {
9186     char *token_1 = "menu.enter_screen.fade_mode";
9187     char *token_2 = "menu.enter_screen.fade_delay";
9188     char *token_3 = "menu.enter_screen.post_delay";
9189     char *token_4 = "menu.leave_screen.fade_mode";
9190     char *token_5 = "menu.leave_screen.fade_delay";
9191     char *token_6 = "menu.leave_screen.post_delay";
9192     char *token_7 = "menu.next_screen.fade_mode";
9193     char *token_8 = "menu.next_screen.fade_delay";
9194     char *token_9 = "menu.next_screen.post_delay";
9195     char *value_1 = getHashEntry(setup_file_hash, token_1);
9196     char *value_2 = getHashEntry(setup_file_hash, token_2);
9197     char *value_3 = getHashEntry(setup_file_hash, token_3);
9198     char *value_4 = getHashEntry(setup_file_hash, token_4);
9199     char *value_5 = getHashEntry(setup_file_hash, token_5);
9200     char *value_6 = getHashEntry(setup_file_hash, token_6);
9201     char *value_7 = getHashEntry(setup_file_hash, token_7);
9202     char *value_8 = getHashEntry(setup_file_hash, token_8);
9203     char *value_9 = getHashEntry(setup_file_hash, token_9);
9204
9205     if (value_1 != NULL)
9206       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9207                                                                  value_1);
9208     if (value_2 != NULL)
9209       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9210                                                                   value_2);
9211     if (value_3 != NULL)
9212       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9213                                                                   value_3);
9214     if (value_4 != NULL)
9215       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9216                                                                  value_4);
9217     if (value_5 != NULL)
9218       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9219                                                                   value_5);
9220     if (value_6 != NULL)
9221       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9222                                                                   value_6);
9223     if (value_7 != NULL)
9224       menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9225                                                                 value_7);
9226     if (value_8 != NULL)
9227       menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9228                                                                  value_8);
9229     if (value_9 != NULL)
9230       menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9231                                                                  value_9);
9232   }
9233
9234   /* special case: initialize with default values that may be overwritten */
9235   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9236   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9237   {
9238     char *token_w1 = "viewport.window.width";
9239     char *token_w2 = "viewport.window.height";
9240     char *token_01 = "viewport.playfield.x";
9241     char *token_02 = "viewport.playfield.y";
9242     char *token_03 = "viewport.playfield.width";
9243     char *token_04 = "viewport.playfield.height";
9244     char *token_05 = "viewport.playfield.border_size";
9245     char *token_06 = "viewport.door_1.x";
9246     char *token_07 = "viewport.door_1.y";
9247     char *token_08 = "viewport.door_1.width";
9248     char *token_09 = "viewport.door_1.height";
9249     char *token_10 = "viewport.door_1.border_size";
9250     char *token_11 = "viewport.door_2.x";
9251     char *token_12 = "viewport.door_2.y";
9252     char *token_13 = "viewport.door_2.width";
9253     char *token_14 = "viewport.door_2.height";
9254     char *token_15 = "viewport.door_2.border_size";
9255     char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9256     char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9257     char *value_01 = getHashEntry(setup_file_hash, token_01);
9258     char *value_02 = getHashEntry(setup_file_hash, token_02);
9259     char *value_03 = getHashEntry(setup_file_hash, token_03);
9260     char *value_04 = getHashEntry(setup_file_hash, token_04);
9261     char *value_05 = getHashEntry(setup_file_hash, token_05);
9262     char *value_06 = getHashEntry(setup_file_hash, token_06);
9263     char *value_07 = getHashEntry(setup_file_hash, token_07);
9264     char *value_08 = getHashEntry(setup_file_hash, token_08);
9265     char *value_09 = getHashEntry(setup_file_hash, token_09);
9266     char *value_10 = getHashEntry(setup_file_hash, token_10);
9267     char *value_11 = getHashEntry(setup_file_hash, token_11);
9268     char *value_12 = getHashEntry(setup_file_hash, token_12);
9269     char *value_13 = getHashEntry(setup_file_hash, token_13);
9270     char *value_14 = getHashEntry(setup_file_hash, token_14);
9271     char *value_15 = getHashEntry(setup_file_hash, token_15);
9272
9273     if (value_w1 != NULL)
9274       viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9275     if (value_w2 != NULL)
9276       viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9277     if (value_01 != NULL)
9278       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9279     if (value_02 != NULL)
9280       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9281     if (value_03 != NULL)
9282       viewport.playfield[i].width = get_token_parameter_value(token_03,
9283                                                               value_03);
9284     if (value_04 != NULL)
9285       viewport.playfield[i].height = get_token_parameter_value(token_04,
9286                                                                value_04);
9287     if (value_05 != NULL)
9288       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9289                                                                     value_05);
9290     if (value_06 != NULL)
9291       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9292     if (value_07 != NULL)
9293       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9294     if (value_08 != NULL)
9295       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9296     if (value_09 != NULL)
9297       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9298     if (value_10 != NULL)
9299       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9300                                                                  value_10);
9301     if (value_11 != NULL)
9302       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9303     if (value_12 != NULL)
9304       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9305     if (value_13 != NULL)
9306       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9307     if (value_14 != NULL)
9308       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9309     if (value_15 != NULL)
9310       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9311                                                                  value_15);
9312   }
9313
9314   /* special case: initialize with default values that may be overwritten */
9315   /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9316   for (i = 0; title_info[i].info != NULL; i++)
9317   {
9318     struct TitleFadingInfo *info = title_info[i].info;
9319     char *base_token = title_info[i].text;
9320
9321     for (j = 0; title_tokens[j].type != -1; j++)
9322     {
9323       char *token = getStringCat2(base_token, title_tokens[j].text);
9324       char *value = getHashEntry(setup_file_hash, token);
9325
9326       if (value != NULL)
9327       {
9328         int parameter_value = get_token_parameter_value(token, value);
9329
9330         tfi = *info;
9331
9332         *(int *)title_tokens[j].value = (int)parameter_value;
9333
9334         *info = tfi;
9335       }
9336
9337       free(token);
9338     }
9339   }
9340
9341   /* special case: initialize with default values that may be overwritten */
9342   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9343   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9344   {
9345     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9346     char *base_token = titlemessage_arrays[i].text;
9347
9348     for (j = 0; titlemessage_tokens[j].type != -1; j++)
9349     {
9350       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9351       char *value = getHashEntry(setup_file_hash, token);
9352
9353       if (value != NULL)
9354       {
9355         int parameter_value = get_token_parameter_value(token, value);
9356
9357         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9358         {
9359           tmi = array[k];
9360
9361           if (titlemessage_tokens[j].type == TYPE_INTEGER)
9362             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
9363           else
9364             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9365
9366           array[k] = tmi;
9367         }
9368       }
9369
9370       free(token);
9371     }
9372   }
9373
9374   /* read (and overwrite with) values that may be specified in config file */
9375   for (i = 0; image_config_vars[i].token != NULL; i++)
9376   {
9377     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9378
9379     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9380     if (value != NULL && !strEqual(value, ARG_DEFAULT))
9381       *image_config_vars[i].value =
9382         get_token_parameter_value(image_config_vars[i].token, value);
9383   }
9384
9385   freeSetupFileHash(setup_file_hash);
9386 }
9387
9388 void LoadMenuDesignSettings()
9389 {
9390   char *filename_base = UNDEFINED_FILENAME, *filename_local;
9391
9392   InitMenuDesignSettings_Static();
9393   InitMenuDesignSettings_SpecialPreProcessing();
9394
9395   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9396   {
9397     /* first look for special settings configured in level series config */
9398     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9399
9400     if (fileExists(filename_base))
9401       LoadMenuDesignSettingsFromFilename(filename_base);
9402   }
9403
9404   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9405
9406   if (filename_local != NULL && !strEqual(filename_base, filename_local))
9407     LoadMenuDesignSettingsFromFilename(filename_local);
9408
9409   InitMenuDesignSettings_SpecialPostProcessing();
9410 }
9411
9412 void LoadMenuDesignSettings_AfterGraphics()
9413 {
9414   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9415 }
9416
9417 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9418 {
9419   char *filename = getEditorSetupFilename();
9420   SetupFileList *setup_file_list, *list;
9421   SetupFileHash *element_hash;
9422   int num_unknown_tokens = 0;
9423   int i;
9424
9425   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9426     return;
9427
9428   element_hash = newSetupFileHash();
9429
9430   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9431     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9432
9433   /* determined size may be larger than needed (due to unknown elements) */
9434   *num_elements = 0;
9435   for (list = setup_file_list; list != NULL; list = list->next)
9436     (*num_elements)++;
9437
9438   /* add space for up to 3 more elements for padding that may be needed */
9439   *num_elements += 3;
9440
9441   /* free memory for old list of elements, if needed */
9442   checked_free(*elements);
9443
9444   /* allocate memory for new list of elements */
9445   *elements = checked_malloc(*num_elements * sizeof(int));
9446
9447   *num_elements = 0;
9448   for (list = setup_file_list; list != NULL; list = list->next)
9449   {
9450     char *value = getHashEntry(element_hash, list->token);
9451
9452     if (value == NULL)          /* try to find obsolete token mapping */
9453     {
9454       char *mapped_token = get_mapped_token(list->token);
9455
9456       if (mapped_token != NULL)
9457       {
9458         value = getHashEntry(element_hash, mapped_token);
9459
9460         free(mapped_token);
9461       }
9462     }
9463
9464     if (value != NULL)
9465     {
9466       (*elements)[(*num_elements)++] = atoi(value);
9467     }
9468     else
9469     {
9470       if (num_unknown_tokens == 0)
9471       {
9472         Error(ERR_INFO_LINE, "-");
9473         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9474         Error(ERR_INFO, "- config file: '%s'", filename);
9475
9476         num_unknown_tokens++;
9477       }
9478
9479       Error(ERR_INFO, "- token: '%s'", list->token);
9480     }
9481   }
9482
9483   if (num_unknown_tokens > 0)
9484     Error(ERR_INFO_LINE, "-");
9485
9486   while (*num_elements % 4)     /* pad with empty elements, if needed */
9487     (*elements)[(*num_elements)++] = EL_EMPTY;
9488
9489   freeSetupFileList(setup_file_list);
9490   freeSetupFileHash(element_hash);
9491
9492 #if 0
9493   for (i = 0; i < *num_elements; i++)
9494     printf("editor: element '%s' [%d]\n",
9495            element_info[(*elements)[i]].token_name, (*elements)[i]);
9496 #endif
9497 }
9498
9499 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9500                                                      boolean is_sound)
9501 {
9502   SetupFileHash *setup_file_hash = NULL;
9503   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9504   char *filename_music, *filename_prefix, *filename_info;
9505   struct
9506   {
9507     char *token;
9508     char **value_ptr;
9509   }
9510   token_to_value_ptr[] =
9511   {
9512     { "title_header",   &tmp_music_file_info.title_header       },
9513     { "artist_header",  &tmp_music_file_info.artist_header      },
9514     { "album_header",   &tmp_music_file_info.album_header       },
9515     { "year_header",    &tmp_music_file_info.year_header        },
9516
9517     { "title",          &tmp_music_file_info.title              },
9518     { "artist",         &tmp_music_file_info.artist             },
9519     { "album",          &tmp_music_file_info.album              },
9520     { "year",           &tmp_music_file_info.year               },
9521
9522     { NULL,             NULL                                    },
9523   };
9524   int i;
9525
9526   filename_music = (is_sound ? getCustomSoundFilename(basename) :
9527                     getCustomMusicFilename(basename));
9528
9529   if (filename_music == NULL)
9530     return NULL;
9531
9532   /* ---------- try to replace file extension ---------- */
9533
9534   filename_prefix = getStringCopy(filename_music);
9535   if (strrchr(filename_prefix, '.') != NULL)
9536     *strrchr(filename_prefix, '.') = '\0';
9537   filename_info = getStringCat2(filename_prefix, ".txt");
9538
9539   if (fileExists(filename_info))
9540     setup_file_hash = loadSetupFileHash(filename_info);
9541
9542   free(filename_prefix);
9543   free(filename_info);
9544
9545   if (setup_file_hash == NULL)
9546   {
9547     /* ---------- try to add file extension ---------- */
9548
9549     filename_prefix = getStringCopy(filename_music);
9550     filename_info = getStringCat2(filename_prefix, ".txt");
9551
9552     if (fileExists(filename_info))
9553       setup_file_hash = loadSetupFileHash(filename_info);
9554
9555     free(filename_prefix);
9556     free(filename_info);
9557   }
9558
9559   if (setup_file_hash == NULL)
9560     return NULL;
9561
9562   /* ---------- music file info found ---------- */
9563
9564   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9565
9566   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9567   {
9568     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9569
9570     *token_to_value_ptr[i].value_ptr =
9571       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9572   }
9573
9574   tmp_music_file_info.basename = getStringCopy(basename);
9575   tmp_music_file_info.music = music;
9576   tmp_music_file_info.is_sound = is_sound;
9577
9578   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9579   *new_music_file_info = tmp_music_file_info;
9580
9581   return new_music_file_info;
9582 }
9583
9584 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9585 {
9586   return get_music_file_info_ext(basename, music, FALSE);
9587 }
9588
9589 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9590 {
9591   return get_music_file_info_ext(basename, sound, TRUE);
9592 }
9593
9594 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9595                                      char *basename, boolean is_sound)
9596 {
9597   for (; list != NULL; list = list->next)
9598     if (list->is_sound == is_sound && strEqual(list->basename, basename))
9599       return TRUE;
9600
9601   return FALSE;
9602 }
9603
9604 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9605 {
9606   return music_info_listed_ext(list, basename, FALSE);
9607 }
9608
9609 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9610 {
9611   return music_info_listed_ext(list, basename, TRUE);
9612 }
9613
9614 void LoadMusicInfo()
9615 {
9616   char *music_directory = getCustomMusicDirectory();
9617   int num_music = getMusicListSize();
9618   int num_music_noconf = 0;
9619   int num_sounds = getSoundListSize();
9620   Directory *dir;
9621   DirectoryEntry *dir_entry;
9622   struct FileInfo *music, *sound;
9623   struct MusicFileInfo *next, **new;
9624   int i;
9625
9626   while (music_file_info != NULL)
9627   {
9628     next = music_file_info->next;
9629
9630     checked_free(music_file_info->basename);
9631
9632     checked_free(music_file_info->title_header);
9633     checked_free(music_file_info->artist_header);
9634     checked_free(music_file_info->album_header);
9635     checked_free(music_file_info->year_header);
9636
9637     checked_free(music_file_info->title);
9638     checked_free(music_file_info->artist);
9639     checked_free(music_file_info->album);
9640     checked_free(music_file_info->year);
9641
9642     free(music_file_info);
9643
9644     music_file_info = next;
9645   }
9646
9647   new = &music_file_info;
9648
9649   for (i = 0; i < num_music; i++)
9650   {
9651     music = getMusicListEntry(i);
9652
9653     if (music->filename == NULL)
9654       continue;
9655
9656     if (strEqual(music->filename, UNDEFINED_FILENAME))
9657       continue;
9658
9659     /* a configured file may be not recognized as music */
9660     if (!FileIsMusic(music->filename))
9661       continue;
9662
9663     if (!music_info_listed(music_file_info, music->filename))
9664     {
9665       *new = get_music_file_info(music->filename, i);
9666
9667       if (*new != NULL)
9668         new = &(*new)->next;
9669     }
9670   }
9671
9672   if ((dir = openDirectory(music_directory)) == NULL)
9673   {
9674     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9675     return;
9676   }
9677
9678   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
9679   {
9680     char *basename = dir_entry->basename;
9681     boolean music_already_used = FALSE;
9682     int i;
9683
9684     /* skip all music files that are configured in music config file */
9685     for (i = 0; i < num_music; i++)
9686     {
9687       music = getMusicListEntry(i);
9688
9689       if (music->filename == NULL)
9690         continue;
9691
9692       if (strEqual(basename, music->filename))
9693       {
9694         music_already_used = TRUE;
9695         break;
9696       }
9697     }
9698
9699     if (music_already_used)
9700       continue;
9701
9702     if (!FileIsMusic(dir_entry->filename))
9703       continue;
9704
9705     if (!music_info_listed(music_file_info, basename))
9706     {
9707       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9708
9709       if (*new != NULL)
9710         new = &(*new)->next;
9711     }
9712
9713     num_music_noconf++;
9714   }
9715
9716   closeDirectory(dir);
9717
9718   for (i = 0; i < num_sounds; i++)
9719   {
9720     sound = getSoundListEntry(i);
9721
9722     if (sound->filename == NULL)
9723       continue;
9724
9725     if (strEqual(sound->filename, UNDEFINED_FILENAME))
9726       continue;
9727
9728     /* a configured file may be not recognized as sound */
9729     if (!FileIsSound(sound->filename))
9730       continue;
9731
9732     if (!sound_info_listed(music_file_info, sound->filename))
9733     {
9734       *new = get_sound_file_info(sound->filename, i);
9735       if (*new != NULL)
9736         new = &(*new)->next;
9737     }
9738   }
9739 }
9740
9741 void add_helpanim_entry(int element, int action, int direction, int delay,
9742                         int *num_list_entries)
9743 {
9744   struct HelpAnimInfo *new_list_entry;
9745   (*num_list_entries)++;
9746
9747   helpanim_info =
9748     checked_realloc(helpanim_info,
9749                     *num_list_entries * sizeof(struct HelpAnimInfo));
9750   new_list_entry = &helpanim_info[*num_list_entries - 1];
9751
9752   new_list_entry->element = element;
9753   new_list_entry->action = action;
9754   new_list_entry->direction = direction;
9755   new_list_entry->delay = delay;
9756 }
9757
9758 void print_unknown_token(char *filename, char *token, int token_nr)
9759 {
9760   if (token_nr == 0)
9761   {
9762     Error(ERR_INFO_LINE, "-");
9763     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9764     Error(ERR_INFO, "- config file: '%s'", filename);
9765   }
9766
9767   Error(ERR_INFO, "- token: '%s'", token);
9768 }
9769
9770 void print_unknown_token_end(int token_nr)
9771 {
9772   if (token_nr > 0)
9773     Error(ERR_INFO_LINE, "-");
9774 }
9775
9776 void LoadHelpAnimInfo()
9777 {
9778   char *filename = getHelpAnimFilename();
9779   SetupFileList *setup_file_list = NULL, *list;
9780   SetupFileHash *element_hash, *action_hash, *direction_hash;
9781   int num_list_entries = 0;
9782   int num_unknown_tokens = 0;
9783   int i;
9784
9785   if (fileExists(filename))
9786     setup_file_list = loadSetupFileList(filename);
9787
9788   if (setup_file_list == NULL)
9789   {
9790     /* use reliable default values from static configuration */
9791     SetupFileList *insert_ptr;
9792
9793     insert_ptr = setup_file_list =
9794       newSetupFileList(helpanim_config[0].token,
9795                        helpanim_config[0].value);
9796
9797     for (i = 1; helpanim_config[i].token; i++)
9798       insert_ptr = addListEntry(insert_ptr,
9799                                 helpanim_config[i].token,
9800                                 helpanim_config[i].value);
9801   }
9802
9803   element_hash   = newSetupFileHash();
9804   action_hash    = newSetupFileHash();
9805   direction_hash = newSetupFileHash();
9806
9807   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
9808     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9809
9810   for (i = 0; i < NUM_ACTIONS; i++)
9811     setHashEntry(action_hash, element_action_info[i].suffix,
9812                  i_to_a(element_action_info[i].value));
9813
9814   /* do not store direction index (bit) here, but direction value! */
9815   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
9816     setHashEntry(direction_hash, element_direction_info[i].suffix,
9817                  i_to_a(1 << element_direction_info[i].value));
9818
9819   for (list = setup_file_list; list != NULL; list = list->next)
9820   {
9821     char *element_token, *action_token, *direction_token;
9822     char *element_value, *action_value, *direction_value;
9823     int delay = atoi(list->value);
9824
9825     if (strEqual(list->token, "end"))
9826     {
9827       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9828
9829       continue;
9830     }
9831
9832     /* first try to break element into element/action/direction parts;
9833        if this does not work, also accept combined "element[.act][.dir]"
9834        elements (like "dynamite.active"), which are unique elements */
9835
9836     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
9837     {
9838       element_value = getHashEntry(element_hash, list->token);
9839       if (element_value != NULL)        /* element found */
9840         add_helpanim_entry(atoi(element_value), -1, -1, delay,
9841                            &num_list_entries);
9842       else
9843       {
9844         /* no further suffixes found -- this is not an element */
9845         print_unknown_token(filename, list->token, num_unknown_tokens++);
9846       }
9847
9848       continue;
9849     }
9850
9851     /* token has format "<prefix>.<something>" */
9852
9853     action_token = strchr(list->token, '.');    /* suffix may be action ... */
9854     direction_token = action_token;             /* ... or direction */
9855
9856     element_token = getStringCopy(list->token);
9857     *strchr(element_token, '.') = '\0';
9858
9859     element_value = getHashEntry(element_hash, element_token);
9860
9861     if (element_value == NULL)          /* this is no element */
9862     {
9863       element_value = getHashEntry(element_hash, list->token);
9864       if (element_value != NULL)        /* combined element found */
9865         add_helpanim_entry(atoi(element_value), -1, -1, delay,
9866                            &num_list_entries);
9867       else
9868         print_unknown_token(filename, list->token, num_unknown_tokens++);
9869
9870       free(element_token);
9871
9872       continue;
9873     }
9874
9875     action_value = getHashEntry(action_hash, action_token);
9876
9877     if (action_value != NULL)           /* action found */
9878     {
9879       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
9880                     &num_list_entries);
9881
9882       free(element_token);
9883
9884       continue;
9885     }
9886
9887     direction_value = getHashEntry(direction_hash, direction_token);
9888
9889     if (direction_value != NULL)        /* direction found */
9890     {
9891       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
9892                          &num_list_entries);
9893
9894       free(element_token);
9895
9896       continue;
9897     }
9898
9899     if (strchr(action_token + 1, '.') == NULL)
9900     {
9901       /* no further suffixes found -- this is not an action nor direction */
9902
9903       element_value = getHashEntry(element_hash, list->token);
9904       if (element_value != NULL)        /* combined element found */
9905         add_helpanim_entry(atoi(element_value), -1, -1, delay,
9906                            &num_list_entries);
9907       else
9908         print_unknown_token(filename, list->token, num_unknown_tokens++);
9909
9910       free(element_token);
9911
9912       continue;
9913     }
9914
9915     /* token has format "<prefix>.<suffix>.<something>" */
9916
9917     direction_token = strchr(action_token + 1, '.');
9918
9919     action_token = getStringCopy(action_token);
9920     *strchr(action_token + 1, '.') = '\0';
9921
9922     action_value = getHashEntry(action_hash, action_token);
9923
9924     if (action_value == NULL)           /* this is no action */
9925     {
9926       element_value = getHashEntry(element_hash, list->token);
9927       if (element_value != NULL)        /* combined element found */
9928         add_helpanim_entry(atoi(element_value), -1, -1, delay,
9929                            &num_list_entries);
9930       else
9931         print_unknown_token(filename, list->token, num_unknown_tokens++);
9932
9933       free(element_token);
9934       free(action_token);
9935
9936       continue;
9937     }
9938
9939     direction_value = getHashEntry(direction_hash, direction_token);
9940
9941     if (direction_value != NULL)        /* direction found */
9942     {
9943       add_helpanim_entry(atoi(element_value), atoi(action_value),
9944                          atoi(direction_value), delay, &num_list_entries);
9945
9946       free(element_token);
9947       free(action_token);
9948
9949       continue;
9950     }
9951
9952     /* this is no direction */
9953
9954     element_value = getHashEntry(element_hash, list->token);
9955     if (element_value != NULL)          /* combined element found */
9956       add_helpanim_entry(atoi(element_value), -1, -1, delay,
9957                          &num_list_entries);
9958     else
9959       print_unknown_token(filename, list->token, num_unknown_tokens++);
9960
9961     free(element_token);
9962     free(action_token);
9963   }
9964
9965   print_unknown_token_end(num_unknown_tokens);
9966
9967   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
9968   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
9969
9970   freeSetupFileList(setup_file_list);
9971   freeSetupFileHash(element_hash);
9972   freeSetupFileHash(action_hash);
9973   freeSetupFileHash(direction_hash);
9974
9975 #if 0
9976   for (i = 0; i < num_list_entries; i++)
9977     printf("::: '%s': %d, %d, %d => %d\n",
9978            EL_NAME(helpanim_info[i].element),
9979            helpanim_info[i].element,
9980            helpanim_info[i].action,
9981            helpanim_info[i].direction,
9982            helpanim_info[i].delay);
9983 #endif
9984 }
9985
9986 void LoadHelpTextInfo()
9987 {
9988   char *filename = getHelpTextFilename();
9989   int i;
9990
9991   if (helptext_info != NULL)
9992   {
9993     freeSetupFileHash(helptext_info);
9994     helptext_info = NULL;
9995   }
9996
9997   if (fileExists(filename))
9998     helptext_info = loadSetupFileHash(filename);
9999
10000   if (helptext_info == NULL)
10001   {
10002     /* use reliable default values from static configuration */
10003     helptext_info = newSetupFileHash();
10004
10005     for (i = 0; helptext_config[i].token; i++)
10006       setHashEntry(helptext_info,
10007                    helptext_config[i].token,
10008                    helptext_config[i].value);
10009   }
10010
10011 #if 0
10012   BEGIN_HASH_ITERATION(helptext_info, itr)
10013   {
10014     printf("::: '%s' => '%s'\n",
10015            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10016   }
10017   END_HASH_ITERATION(hash, itr)
10018 #endif
10019 }
10020
10021
10022 /* ------------------------------------------------------------------------- */
10023 /* convert levels                                                            */
10024 /* ------------------------------------------------------------------------- */
10025
10026 #define MAX_NUM_CONVERT_LEVELS          1000
10027
10028 void ConvertLevels()
10029 {
10030   static LevelDirTree *convert_leveldir = NULL;
10031   static int convert_level_nr = -1;
10032   static int num_levels_handled = 0;
10033   static int num_levels_converted = 0;
10034   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10035   int i;
10036
10037   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10038                                                global.convert_leveldir);
10039
10040   if (convert_leveldir == NULL)
10041     Error(ERR_EXIT, "no such level identifier: '%s'",
10042           global.convert_leveldir);
10043
10044   leveldir_current = convert_leveldir;
10045
10046   if (global.convert_level_nr != -1)
10047   {
10048     convert_leveldir->first_level = global.convert_level_nr;
10049     convert_leveldir->last_level  = global.convert_level_nr;
10050   }
10051
10052   convert_level_nr = convert_leveldir->first_level;
10053
10054   PrintLine("=", 79);
10055   Print("Converting levels\n");
10056   PrintLine("-", 79);
10057   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10058   Print("Level series name:       '%s'\n", convert_leveldir->name);
10059   Print("Level series author:     '%s'\n", convert_leveldir->author);
10060   Print("Number of levels:        %d\n",   convert_leveldir->levels);
10061   PrintLine("=", 79);
10062   Print("\n");
10063
10064   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10065     levels_failed[i] = FALSE;
10066
10067   while (convert_level_nr <= convert_leveldir->last_level)
10068   {
10069     char *level_filename;
10070     boolean new_level;
10071
10072     level_nr = convert_level_nr++;
10073
10074     Print("Level %03d: ", level_nr);
10075
10076     LoadLevel(level_nr);
10077     if (level.no_level_file || level.no_valid_file)
10078     {
10079       Print("(no level)\n");
10080       continue;
10081     }
10082
10083     Print("converting level ... ");
10084
10085     level_filename = getDefaultLevelFilename(level_nr);
10086     new_level = !fileExists(level_filename);
10087
10088     if (new_level)
10089     {
10090       SaveLevel(level_nr);
10091
10092       num_levels_converted++;
10093
10094       Print("converted.\n");
10095     }
10096     else
10097     {
10098       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10099         levels_failed[level_nr] = TRUE;
10100
10101       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10102     }
10103
10104     num_levels_handled++;
10105   }
10106
10107   Print("\n");
10108   PrintLine("=", 79);
10109   Print("Number of levels handled: %d\n", num_levels_handled);
10110   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10111          (num_levels_handled ?
10112           num_levels_converted * 100 / num_levels_handled : 0));
10113   PrintLine("-", 79);
10114   Print("Summary (for automatic parsing by scripts):\n");
10115   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10116          convert_leveldir->identifier, num_levels_converted,
10117          num_levels_handled,
10118          (num_levels_handled ?
10119           num_levels_converted * 100 / num_levels_handled : 0));
10120
10121   if (num_levels_handled != num_levels_converted)
10122   {
10123     Print(", FAILED:");
10124     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10125       if (levels_failed[i])
10126         Print(" %03d", i);
10127   }
10128
10129   Print("\n");
10130   PrintLine("=", 79);
10131
10132   CloseAllAndExit(0);
10133 }
10134
10135
10136 /* ------------------------------------------------------------------------- */
10137 /* create and save images for use in level sketches (raw BMP format)         */
10138 /* ------------------------------------------------------------------------- */
10139
10140 void CreateLevelSketchImages()
10141 {
10142 #if defined(TARGET_SDL)
10143   Bitmap *bitmap1;
10144   Bitmap *bitmap2;
10145   int i;
10146
10147   InitElementPropertiesGfxElement();
10148
10149   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10150   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10151
10152   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10153   {
10154     Bitmap *src_bitmap;
10155     int src_x, src_y;
10156     int element = getMappedElement(i);
10157     int graphic = el2edimg(element);
10158     char basename1[16];
10159     char basename2[16];
10160     char *filename1;
10161     char *filename2;
10162
10163     sprintf(basename1, "%03d.bmp", i);
10164     sprintf(basename2, "%03ds.bmp", i);
10165
10166     filename1 = getPath2(global.create_images_dir, basename1);
10167     filename2 = getPath2(global.create_images_dir, basename2);
10168
10169     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10170     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10171                0, 0);
10172
10173     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10174       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10175
10176     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10177     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10178
10179     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10180       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10181
10182     free(filename1);
10183     free(filename2);
10184
10185     if (options.debug)
10186       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10187   }
10188
10189   FreeBitmap(bitmap1);
10190   FreeBitmap(bitmap2);
10191
10192   if (options.debug)
10193     printf("\n");
10194
10195   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10196
10197   CloseAllAndExit(0);
10198 #endif
10199 }
10200
10201
10202 /* ------------------------------------------------------------------------- */
10203 /* create and save images for custom and group elements (raw BMP format)     */
10204 /* ------------------------------------------------------------------------- */
10205
10206 void CreateCustomElementImages(char *directory)
10207 {
10208 #if defined(TARGET_SDL)
10209   char *src_basename = "RocksCE-template.ilbm";
10210   char *dst_basename = "RocksCE.bmp";
10211   char *src_filename = getPath2(directory, src_basename);
10212   char *dst_filename = getPath2(directory, dst_basename);
10213   Bitmap *src_bitmap;
10214   Bitmap *bitmap;
10215   int yoffset_ce = 0;
10216   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10217   int i;
10218
10219   SDLInitVideoDisplay();
10220
10221   ReCreateBitmap(&backbuffer, video.width, video.height);
10222
10223   src_bitmap = LoadImage(src_filename);
10224
10225   bitmap = CreateBitmap(TILEX * 16 * 2,
10226                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10227                         DEFAULT_DEPTH);
10228
10229   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10230   {
10231     int x = i % 16;
10232     int y = i / 16;
10233     int ii = i + 1;
10234     int j;
10235
10236     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10237                TILEX * x, TILEY * y + yoffset_ce);
10238
10239     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10240                TILEX, TILEY,
10241                TILEX * x + TILEX * 16,
10242                TILEY * y + yoffset_ce);
10243
10244     for (j = 2; j >= 0; j--)
10245     {
10246       int c = ii % 10;
10247
10248       BlitBitmap(src_bitmap, bitmap,
10249                  TILEX + c * 7, 0, 6, 10,
10250                  TILEX * x + 6 + j * 7,
10251                  TILEY * y + 11 + yoffset_ce);
10252
10253       BlitBitmap(src_bitmap, bitmap,
10254                  TILEX + c * 8, TILEY, 6, 10,
10255                  TILEX * 16 + TILEX * x + 6 + j * 8,
10256                  TILEY * y + 10 + yoffset_ce);
10257
10258       ii /= 10;
10259     }
10260   }
10261
10262   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10263   {
10264     int x = i % 16;
10265     int y = i / 16;
10266     int ii = i + 1;
10267     int j;
10268
10269     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10270                TILEX * x, TILEY * y + yoffset_ge);
10271
10272     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10273                TILEX, TILEY,
10274                TILEX * x + TILEX * 16,
10275                TILEY * y + yoffset_ge);
10276
10277     for (j = 1; j >= 0; j--)
10278     {
10279       int c = ii % 10;
10280
10281       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10282                  TILEX * x + 6 + j * 10,
10283                  TILEY * y + 11 + yoffset_ge);
10284
10285       BlitBitmap(src_bitmap, bitmap,
10286                  TILEX + c * 8, TILEY + 12, 6, 10,
10287                  TILEX * 16 + TILEX * x + 10 + j * 8,
10288                  TILEY * y + 10 + yoffset_ge);
10289
10290       ii /= 10;
10291     }
10292   }
10293
10294   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10295     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10296
10297   FreeBitmap(bitmap);
10298
10299   CloseAllAndExit(0);
10300 #endif
10301 }