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