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